C++模板—参数包

1. 简介

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

2. 语法

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

Type... Args

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

(2) 类型模板参数包:

typename... Types

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

template  typename... Types

(4) 函数参数包:

Types... Args

(5) 参数包扩展:

pattern...

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

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

template struct Invalid; // Error: Ts.. not at the end
 
template
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    // (2)
void f(Ts...) {}            // (4)
template        // (2),(1)
void g(Ts (&...arr)[N]) {}                // (4)

int n[1];
g("a", n); // Ts (&...arr)[N] expands to 
                            // const char (&)[2], int(&)[1]

注意:不允许使用 Ts (&...)[N],因为 C++11 语法要求圆括号括起的 ... 需要有一个名字.

例子2:

#include 

void g(int& i)
{
    std::cout << "lvalue reference: " << i << '\n';
}

void g(int&& i)
{
    std::cout << "rvalue reference: " << i << '\n';
}

template 
void f(T&& arg)
{
    g(std::forward(arg));
}

template 
void f(T&& arg0, Ts&&... args)
{
    g(std::forward(arg0));
    f(std::forward(args)...);    // pattern 为 std::forward(args)
}

int main()
{
    int a = 1, b = 2, c = 3;
    f(a, b, c);
    std::cout << "----------------------\n";
    f(1, 2, 3);
}
lvalue reference: 1
lvalue reference: 2
lvalue reference: 3
----------------------
rvalue reference: 1
rvalue reference: 2
rvalue reference: 3

此处结合完美转发使用.

例子3:

#include 

void myPrintf(const char* fmt)
{
    std::cout << fmt;
}

template 
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 struct Tuple {};
    template struct Pair {};
    
    template struct zip {
      template struct with {
          typedef Tuple...> type;      // Pair is the pattern
      };
    };
    
    typedef zip::with::type T1;
  • 当参数包嵌套时,内层的参数包先扩展,然后再和外层的参数包一起扩展.

    template
    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(&X1), const_cast(&X2), const_cast(&X3))
    f(const_cast(&args)...);
  • 圆括号初始化器

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

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

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

  • 模板实参列表

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

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

    template
    struct value_holder
    {
      template // expands to a non-type template parameter list,
      struct apply {};     // such as 
    };
  • 基类列表、成员初始化列表

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

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

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

你可能感兴趣的:(c++模板)