5.6 变长参数模板
C++11之前的类和函数模板,只能接受一组固定数目的模板参数,变长参数模板这个新特性可以在定义模板函数或模板类时,使用任意个数及任意类别的模板参数,不必在定义时固定参数个数。变长参数模板在使用的时候比较麻烦,语法也比较难以理解。这里介绍4个知识点:使用变长参数模板类、使用变长参数模板函数、解析变长参数模板、变长参数模板的其他操作与技巧。
定义变长参数模板只需要在模板类型添加...即可,在使用时传入任意个数和类型的模板参数。下面的代码演示了一个变长参数模板类的定义和使用,在定义和使用模板时都比较方便,但最麻烦的是如何获取模板内的参数。
//定义一个变长参数模板类 A template<typename ... Types> class A { public: A(Types ... types) { cout << types... << endl; } A<int, float> a1(1, 3.5); A<bool> a2(true);
变长参数模板类的定义和类差不多,但使用起来比类更方便。这里注意一下格式,做参数传入时,格式为 Types... types,可以把Types...视为类型,types视为参数。使用时,格式为types...,注意...的位置!
template<typename... Types> void fun(Types ... types); fun(1, 2, 3, vector<int> a, true); fun("^_^");
传了各种参数进去之后,要如何解析出来呢?解析的过程称为扩展参数包,需要靠另外一个函数来取出。例如下面的代码:
void fun2(int a, float b); template<typename ... Types> void fun1(Types ... types) { fun2(types); } fun1(1, 2.0f);
还可以递归取出变量,下面的写法在每次调用时都会将参数包进行拆分,而拆分的规则由程序员来定义,下面的代码将参数包的变量逐个取出(也可以一次性取出多个),直到包为空。一个空的fun2的作用是为了处理包为空的情况。下面的代码递归调用了fun2,从而将所有的参数逐个地输出。
void fun2() { cout << "finish" << endl; } template<typename ... Types> void fun2(int i, Types ... types) { cout << i << endl; fun2(types); } template<typename ... Types> void fun1(Types ... types) { fun2(types); } //打印出 1 2 3 4 finish fun1(1, 2, 3, 4);
第一次调用fun2时传入的参数包为(1,2,3,4),fun2要求传入一个int,这时fun2接到的内容是1和参数包(2,3,4);第二次接到的内容是2和参数包(3,4),依次类推,到最后包为空的时候,进不去fun2(int i, Types... types),所以需要添加无参数的fun2。程序员可以自定义解析函数,按照自己想要的规则来进行递归。
需要注意的是编写好的变长参数模板代码,如果有问题但没有被代码调用到,则是不会报错的(编译时进行推导失败才会报编译错误)。
使用sizeof...(types)可以计算出传入的参数个数,这个表达式是编译期的常数,该功能很实用,例如,可以用它清理掉前面多出来的空fun2,以及根据长度来判断调用fun2还是fun3或fun4。
template<typename ... Types> void fun2(int i, Types ... types) { cout << i << endl; if(0 < sizeof...(types)) { fun2(types); } }
fun2将int参数逐个取出,如果参数不是int呢?对于这种情况可以用T来代替int,也可以重载多个fun2,如fun2(float, Types... types),fun2(bool, Types... types)。