1. 概述
c++11的新特性中增加了可变参数模板,允许模板定义中包含0到任意个模板参数。
声明可变参数模板时需要在typename或class后面带上省略号"...".
省略号的作用有两个:
1) 声明一个参数包,这个参数包中可以包含0到任意个模板参数。
2) 在模板定义的右边,可以将参数包展开成一个个独立的参数。
2. 定义
可变参数模板的定义如下:
template <class... T>
void func(T... args)
{
cout << sizeof...(args) << endl;
}
int main()
{
func();
func(1);
func(1, 2);
system("pause");
return 0;
}
打印结果:
0
1
2
3. 参数包使用
如果想用参数包的参数,需要将参数包展开。参数包展开的方法有两种:
1)通过递归的模板参数
//递归终止函数
void stop()
{
cout << "stop" << endl;
}
//展开函数
template <class T, class... Args>
void stop(T t, Args... arg)
{
cout << t << endl;
stop(arg...);
}
int main()
{
stop(1, 2, 3);
system("pause");
return 0;
}
打印结果:
1
2
3
stop
其中参数包Args...在展开的过程中递归调用自己,每调用一次参数包中的参数就会少一个,直到所有参数都展开为止。
2)通过逗号表达式和初始化列表
由上例可以看出,递归函数展开参数包必须有一个重载的递归终止函数,即必须有一个同名的中止函数来终止递归。我们可以通过逗号表达式和初始化列表来优化这种方式。
template <class...Args>
void expend(Args...arg)
{
std::initializer_list<int>{ ([&] {cout << arg << endl; }(), 0)...}; //initializer_list初始化一个变长数组
}
int main()
{
expend(1, 2, 3);
system("pause");
return 0;
}
3. 可变参数模板类
模板递归和特化方式展开参数包实例:
//声明可变参数模板类
template <typename... Args> struct Sum;
//Sum类的定义 integral_constant获取编译器常量的特性
template <typename First, typename... Rest>
struct Sum<First, Rest...> : std::integral_constant<int, Sum<First>::value + Sum<Rest...>::value>
{
};
//特化的递归终止类
template<typename Last>
struct Sum<Last> : std::integral_constant<int, sizeof(Last)>
{
};
int main()
{
int i = Sum<int, double, float>::value;
cout << i << endl;
system("pause");
return 0;
}
打印结果:
16
4. 可变参数模板的应用
优化对象创建工厂函数:
template <class... Args>
T* Instance(Args&&... args)
{
return new T(std::forward<Args>(args)...)
}
这里利用完美转发(std::forward)来消除值拷贝时的损耗。