1. 参数包(parameter pack)
(1)模板参数包(以tuple为例):template<typename… Elements>class tuple
①Elements标识符的左侧使用了省略号,在C++11中Elements被称为“模板参数包”,表示可以接受任意多个参数作为模板参数。
②编译器将多个模板参数打包成“单个”的模板参数包,如tuple<int, char, double>实例化模板类时,Element就是包含int、char和double三种类型的集合。
③模板参数包可以是类类型和非类类型的,还可以是模板类型的。非类型的模板参数包如template<int…A> class Test{};其中的A是int类型,而不是类类型。
(2)函数参数包:如template<typename…T> void f(T…args);
①args被称为函数参数包,表示函数可以接受多个任意类型的参数。(T被声明为模板参数包,注意与args的不同!)
②在C++11标准中,要求函数参数包必须唯一,且是函数的最后一个参数(模板参数包没有这样的要求)
(3)关于省略号“…”
①当声明一个变量(或标识符)为可变参数时(即变量是一个形参),省略号位于该变量的左侧。如template< typename… Elements >中的Elements,以及template<typename…T> void f(T…args)中T和args都被声明为可变参数,因此省略号位于这些变量的左侧,表明它们是一个参数包。
②当使用参数包时(即变量作为实参),省略号位于参数名称的右侧,表示立即展开该参数,这个过程也被称为解包。如template<typename…A> class Test : public Demo<A…>{}里面的“A…”,很明显A在typename里被声明为可变模板参数,而Demo中己经是在使用而不是在定义这个变量,所以省略号位于变量A的右侧。
2. 几种常见的包扩展表达式
(1)Args&&…:包扩展解包后等价于:Arg1&&,…Argn&&。
(2)template<typename…A> class Test: private Demo<A>…{}和template<typename…A> class Test: private Demo<A…>{}的区别,当实例化Test<X,Y>后,
①前者等价于template<typename…A> class Test : private Demo<X>, private Demo<Y>的多重继承形式。
②后者相当于template<typename…A> class Test : private Demo<X, Y>,即派生于多参数的模板类。
(3)设args被声明为一个函数参数包,则
①printArgs(args…):相当于printArgs(args1,args2,…,argsN)。
②printArgs(args)…:相当于printArgs(args1),…, printArgs(argsN)
③(printArgs(args),0)…:这是一个逗号表达式。相当于(printArgs(args1),0),…(printArgs(argsN),0)
【小结】包扩展表达式“exp…”相当于将省略号左侧的参数包exp(可能是个变量或表达式)视为一个整体来进行扩展。
【实例分析】参数包和包扩展
//example 1: template<typename T, typename... Args> //声明Args为模板参数包,省略号位地参数名称的左侧 void Print(T t, Args... args) //声明args为函数参数包,省略号位于参数名称的左侧 { cout << t; Print(args...); //使用args参数后(注意省略号在右侧),解包后为Print(arg1,arg2,...argN); } //example 2: template<class T> void printarg(T t){} template<class ...Args> //声明Args为模板参数包, void expand(Args... args) //声明args为函数参数包 { int arr[] = {(printarg(args),0)...}; //(printarg(args),0)为逗号表达式,后面加省略号相当 //于:(printarg(args1),0),...(printarg(argsN),0) } //example 3: template<typename ... T> void Wraper(T... t){} //包装器 template<typename T> T printA(T t) //打印参数 { cout << t; return t; } template<typename... Args> void printArgs(Args... args) { Wraper(printA(args)...); //包扩展解包为:Wrap(printA(arg1),...,printA(argN)); //注意printA的返回值为参数本身的类型。 } //example 4: template <typename First, typename... Rest> //声明Rest模板参数包 struct Sum<First, Rest...> //Rest前面己经声明好,这里是在使用Rest(省略后在右侧) { //相当于Sum<First, Rest1,...,RestN> enum{value = Sum<First>::value + Sum<Rest...>::value;}; //使用Rest(省略后在右侧) } //example 5: template<typename ...Types> //Types为模板参数包 void func1(std::vector<Types...> v1); //注意,v1不是函数参数包 template<typename ...Types> //Types为模板参数包 void func1(std::vector<Types>... v2); //注意,v2是函数参数包(...应位于“top-level”) //example 6: template <typename... A> class Test : private B<A>...{}; class Test<X, Y> test; //<==>class Test<X, Y> : private B<X>, private B<Y>{}
template<typename T1, typename T2> class B{}; template <typename... A> class Test : private B<A...>{}; class Test<X, Y> test //<==>class Test<X, Y> : private B<X, Y>{}