精通Cocos2d-x游戏开发(基础卷)
上QQ阅读APP看书,第一时间看更新

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)。