赞
踩
C++11的新特性–可变模版参数(variadic templates)是C++11新增的最强大的特性之一,它对参数进行了高度泛化,它能表示0到任意个数、任意类型的参数。
可变参数模板语法:
template <class... T>
void f(T... args);
上面的可变模版参数的定义当中,省略号的作用有两个:
上面的参数args前面有省略号,所以它就是一个可变模版参数,我们把带省略号的参数称为“参数包”,它里面包含了0到N(N>=0)个模版参数。我们无法直接获取参数包args中的每个参数的,只能通过展开参数包的方式来获取参数包中的每个参数,这是使用可变模版参数的一个主要特点,也是最大的难点,即如何展开可变模版参数。
可变模版参数和普通的模版参数语义是一致的,所以可以应用于函数和类,即可变模版参数函数和可变 模版参数类,然而,模版函数不支持偏特化,所以可变模版参数函数和可变模版参数类展开可变模版参数的方法还不尽相同,下面我们来分别看看他们展开可变模版参数的方法。
#include <iostream>
using namespace std;
template <class... T>
void func(T... args)
{
cout<<sizeof...(args)<<endl;
}
int main(int argc,char **argv)
{
func();
func(1,2);
func(1,2.5,"ds");
return 0;
}
输出:
0
2
3
上面的例子中,f()没有传入参数,所以参数包为空,输出的size为0,后面两次调用分别传入两个和三个参数,故输出的size分别为2和3。由于可变模版参数的类型和个数是不固定的,所以我们可以传任意类型和个数的参数给函数f。这个例子只是简单的将可变模版参数的个数打印出来,如果我们需要将参数包中的每个参数打印出来的话就需要通过一些方法了。
展开可变模版参数函数的方法一般有两种:
通过递归函数展开参数包,需要提供一个参数包展开的函数和一个递归终止函数,递归终止函数正是用来终止递归的。
#include <iostream>
using namespace std;
//递归终止函数
void print()
{
cout<<"empty"<<endl;
}
//展开函数
template <class T,class... args>
void print(T head,args... reset)
{
cout<<"parameter:"<<head<<endl;
print(reset...);
}
int main()
{
print(1,2,3,4,5.5,"daklj");
return 0;
}
上例会输出每一个参数,直到为空时输出empty。展开参数包的函数有两个,一个是递归函数,另外一 个是递归终止函数,参数包Args…在展开的过程中递归调用自己,每调用一次参数包中的参数就会少一 个,直到所有的参数都展开为止,当没有参数时,则调用非模板函数print终止递归过程。
上面的递归终止函数还可以写成这样:
template <class T>
void print(T t)
{
cout<<t<<end;
}
递归函数展开参数包是一种标准做法,也比较好理解,但也有一个缺点,就是必须要一个重载的递归终止 函数,即必须要有一个同名的终止函数来终止递归,这样可能会感觉稍有不便。有没有一种更简单的方 式呢?其实还有一种方法可以不通过递归方式来展开参数包,这种方式需要借助逗号表达式和初始化列 表。比如前面print的例子可以改成这样:
#include <iostream>
using namespace std;
template<class T>
void printarg(T t)
{
cout<<t<<endl;
}
template <class ...Args>
void expand(Args... args)
{
int arr[]={(printarg(args),0)...};
}
int main()
{
expand(1,2,3,4,5);
return 0;
}
这个例子将分别打印出1,2,3,4,5 五个数字。这种展开参数包的方式,不需要通过递归终止函数,是直接在expand函数体中展开的, printarg不是一个递归终止函数,只是一个处理参数包中每一个参数的函数。
expand函数中的逗号表达式:(printarg(args), 0)
,先执行printarg(args)
,再得到逗号表达式的结果0。同时还用到了C++11的另外一个特性——初始化列表,通过初始化列表来初始化一个变长数组, {(printarg(args), 0)...}
将会展开成((printarg(arg1),0), (printarg(arg2),0), (printarg(arg3),0), etc... )
,最 终会创建一个元素值都为0的数组int arr[sizeof...(Args)]
。由于是逗号表达式,在创建数组的过程中会先 执行逗号表达式前面的部分printarg(args)
打印出参数,也就是说在构造int数组的过程中就将参数包展开 了,这个数组的目的纯粹是为了在数组构造的过程展开参数包。我们可以把上面的例子再进一步改进一 下,将函数作为参数,就可以支持lambda表达式了,从而可以少写一个递归终止函数了,具体代码如 下:
#include <iostream>
using namespace std;
template <class F,class... Args>void expand(const F& f,Args&&...args)
{
initializer_list<int>{(f(forward<Args>(args)),0)...};
}
int main()
{
expand([](int i){cout<<i<<endl;},1,2,3);
return 0;
}
C++11的可变模板参数是一项强大的特性,它使得模板编程更加灵活和高效。通过使用可变参数模板,我们可以轻松地处理不同长度的参数列表,在编写泛型代码时更具通用性。无需担心参数列表过长,可变模板参数可以让我们在编写模板时更加灵活,并且能够应对各种不同的情况,从而提高代码的复用性和可读性。掌握了C++11的可变模板参数,可以让我们在模板编程中游刃有余,提升编程技能,同时也能够更好地应对复杂的编程需求。
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。