1. 简介

模板参数包是承受零个或多个模板参数(非类型、类型或模板)的模板参数。
函数参数包是承受零个或多个函数参数的函数参数。

2. 语法

(1) 非类型模板参数包:

Type... Args

此处 Type 为具体的类型,如 int.

(2) 类型模板参数包:

typename... Types

(3) 模板模板参数包:(即模板形参是另一个模板)

template <parameter-list> typename... Types

(4) 函数参数包:

Types... Args

(5) 参数包扩大:

pattern...

pattern 扩大为以逗号分隔的列表(蕴含0或多个模式). 其中,pattern 必须蕴含至多一个参数包.

在类模板中,模板参数包需是最初一个模板形参. 而在函数模板中则不用(指的是在模板形参列表中的地位,而不是函数形参列表),只有可能从函数实参列表推断出参数包即可.

template<typename... Ts, typename U> struct Invalid; // Error: Ts.. not at the end template<typename... Ts, typename U, typename=void>void valid(U, Ts...);     // OK: can deduce U// void valid(Ts..., U);  // Can't be used: Ts... is a non-deduced context in this position

从上例中能够看出,即便是在函数模板中,参数包仍须要是函数形参列表的最初一个参数.
简略起见,尽量将参数包放在最初.

例子1:

template<typename... Ts>    // (2)void f(Ts...) {}            // (4)
template<typename... Ts, int... N>        // (2),(1)void g(Ts (&...arr)[N]) {}                // (4)int n[1];g<const char, int>("a", n); // Ts (&...arr)[N] expands to                             // const char (&)[2], int(&)[1]

留神:不容许应用 Ts (&...)[N],因为 C++11 语法要求圆括号括起的 ... 须要有一个名字.

例子2:

#include <iostream>void g(int& i){    std::cout << "lvalue reference: " << i << '\n';}void g(int&& i){    std::cout << "rvalue reference: " << i << '\n';}template <typename T>void f(T&& arg){    g(std::forward<T>(arg));}template <typename T, typename... Ts>void f(T&& arg0, Ts&&... args){    g(std::forward<T>(arg0));    f(std::forward<Ts>(args)...);    // pattern 为 std::forward<Ts>(args)}int main(){    int a = 1, b = 2, c = 3;    f(a, b, c);    std::cout << "----------------------\n";    f(1, 2, 3);}
lvalue reference: 1lvalue reference: 2lvalue reference: 3----------------------rvalue reference: 1rvalue reference: 2rvalue reference: 3

此处联合完满转发应用.

例子3:

#include <iostream>void myPrintf(const char* fmt){    std::cout << fmt;}template <typename T, typename... Ts>void myPrintf(const char* fmt, T value, Ts... args){    for (; *fmt != '\0'; fmt++)    {        if (*fmt == '%')        {            std::cout << value;            myPrintf(fmt+1, args...);            return;        }        std::cout << *fmt;    }}int main(){    myPrintf("Hello, I'm %, % yeas old.", "gzming", 20);}
Hello, I'm gzming, 20 yeas old.

3. 参数包扩大

  • pattern 中蕴含多个参数包时,这些参数包的长度应该统一.

    template<typename...> struct Tuple {};template<typename T1, typename T2> struct Pair {};template<class ...Args1> struct zip {  template<class ...Args2> struct with {      typedef Tuple<Pair<Args1, Args2>...> type;      // Pair<Args1, Args2> is the pattern  };};typedef zip<short, int>::with<unsigned short, unsigned>::type T1;
  • 当参数包嵌套时,内层的参数包先扩大,而后再和外层的参数包一起扩大.

    template<class... Args>void g(Args... args) {  /*  inner pack expansion is "args...", it is expanded first  outer pack expansion is h(E1, E2, E3) + args..., it is expanded  second (as h(E1,E2,E3) + E1, h(E1,E2,E3) + E2, h(E1,E2,E3) + E3)  */  f(h(args...) + args...);}

4. 能够应用参数包扩大的中央

  • 函数实参列表

    f(&args...);f(++args..., n);// f(const_cast<const E1*>(&X1), const_cast<const E2*>(&X2), const_cast<const E3*>(&X3))f(const_cast<const Args*>(&args)...);
  • 圆括号初始化器

    Class c1(&args...);
  • 花括号初始化器

    template<typename... Ts> void func(Ts... args) {  const int size = sizeof...(args) + 2;  int res[size] = { 1,args...,2 };}

    sizeof... 运算符返回参数包的大小.

  • 模板实参列表

    template<class A, class B, class...C>void func(A arg1, B arg2, C...arg3){  container<A, B, C...> t1;   container<C..., A, B> t2;   container<A, C..., B> t3;}
  • 函数形参列表

    template<typename ...Ts> void f(Ts...) {}
  • 模板形参列表

    template<typename... T>struct value_holder{  template<T... Values> // expands to a non-type template parameter list,  struct apply {};     // such as <int, char, int(&)[5]>};
  • 基类列表、成员初始化列表

    template<class... Mixins>class X : public Mixins... { public:  X(const Mixins&... mixins) : Mixins(mixins)... { }};
  • Lambda 捕捉

    template<class... Args>void f(Args... args) {  auto lm = [&, args...]{ return g(args...); };  lm();}
  • sizeof... 运算符

    template<class... Types>struct count {  static const std::size_t n = sizeof...(Types);};