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: 1
lvalue reference: 2
lvalue reference: 3
----------------------
rvalue reference: 1
rvalue reference: 2
rvalue 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); };