浅墨浓香

想要天亮进城,就得天黑赶路。

导航

第20课 可变参数模板(1)_初识参数包

Posted on 2017-11-04 21:42  浅墨浓香  阅读(1420)  评论(0编辑  收藏  举报

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<typenameT> void f(Targs);

  ①args被称为函数参数包,表示函数可以接受多个任意类型的参数。(T被声明为模板参数包,注意与args的不同!

  ②在C++11标准中,要求函数参数包必须唯一,且是函数的最后一个参数(模板参数包没有这样的要求)

(3)关于省略号“…”

  ①当声明一个变量(或标识符)为可变参数时(即变量是一个形参),省略号位于该变量的左侧。如template< typename Elements >中的Elements,以及template<typenameT> void f(Targs)中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>{}