今天看FireBreath框架时,发现里面有个variant_list_of的函数,这个函数的作用有些独特,可以保存任意个函数参数,看下面的代码:

1
2
VariantList var = variant_list_of(1)(2)(3); //生成3个元素的VariantList
VariantList var = variant_list_of(1); //生成1个元素的VariantList

就是说需要多少个元素只需要在variant_list_of函数后面添加多少个()即可。
觉得这个函数非常有趣,有些像C语言中的可变参数函数,于是看了一下FireBreath的源码中的实现。
后来发现FireBreath是通过重载()运算符实现这个功能的
variant_list_of函数定义如下:

1
2
3
4
5
inline FB::detail::VariantListInserter
variant_list_of(FB::variant v)
{
return FB::detail::VariantListInserter(v);
}

可见它是返回一个VariantListInserter对象。而对象class定义如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
class VariantListInserter
{
public:
VariantListInserter() : m_l() {}
VariantListInserter(FB::variant v) : m_l(1, v) {}
VariantListInserter& operator()(FB::variant v)
{
m_l.insert(m_l.end(), v);
return *this;
}
operator FB::VariantList& ()
{
return m_l;
}
operator FB::variant()
{
return FB::variant(m_l);
}
private:
FB::VariantList m_l;
};

很简单的一个辅助类,唯一成员就是VariantList, VariantList在源码就是一个vector<variant>

从上述代码很容易看出是如何实现的,以下这句代码为例子说明

1
VariantList var = variant_list_of(1)(2)(3);
  1. 首先执行variant_list_of,该函数以(1)为函数参数,返回一个VariantInserter类
  2. 经过step1后,此时上面的代码变成VariantListInsterter(2)(3),由于重载了()运算符,那么就会依次调用()运算符,从(2)到(3)
  3. 经过两次的()运算符调用后,因此等式右边类型仍然为VariantListInserter,但是该类重载了类型转换操作符(代码:operator FB::VariantList& ()),这时赋值前就会把类型转换层VariantList后再赋值给var.

不得不感叹其中巧妙之处:利用()运算符,在调用1次()后返回自身再依次调用后面的(),连锁下去。这样就可以保存任意参数。

下面是上述的简化版代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
class VaraintList
{
public:
VaraintList(int a)
{
var_list.push_back(a);
}
VaraintList& operator() (int a)
{
var_list.push_back(a);
return *this;
}
operator vector<int>& ()
{
return var_list;
}
private:
vector<int> var_list;
};
VaraintList varaint_list_of(int a)
{
return VaraintList(a);
}