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


    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>{}
  • 相关阅读:
    数据结构--实验5---排序(c)
    数据结构---实验4--查找(c)
    Tornado 模板(StaticFileHandler/static_path/template_path等) 笔记
    tornado请求头/状态码/接口 笔记
    Tornado 文件操作笔记
    tornado输入-get_query_argument()等 笔记
    tornado 笔记
    干货-递归下降分析器 笔记(具体看Python Cookbook, 3rd edition 的2.19章节)
    xml.sax 笔记
    HTMLParser 笔记
  • 原文地址:https://www.cnblogs.com/5iedu/p/7784721.html
Copyright © 2020-2023  润新知