Parameter pack
参数包
模板参数包是接受零或多个模板参数%28非类型、类型或模板%29的模板参数。函数参数包是接受零或多个函数参数的函数参数。
具有至少一个参数包的模板称为可变模板
...
句法
模板参数Pack%28出现在类模板在一个功能模板参数列表%29。
type ... Args(optional) | (1) | (since C++11) |
---|---|---|
typename|class ... Args(optional) | (2) | (since C++11) |
template < parameter-list > typename(C++17)|class ... Args(optional) | (3) | (since C++11) |
函数参数包%28a形式报关员出现在变量函数模板%29的函数参数列表中。
Args ... args(optional) | (4) | (since C++11) |
---|
参数包展开%28出现在变量模板%29的主体中。
pattern ... | (5) | (since C++11) |
---|
1%29带有可选名称的非类型模板参数包。
2%29带有可选名称的类型模板参数包
3%29模板参数包具有可选名称
4%29带有可选名称的函数参数包
5%29参数包展开:展开为逗号分隔的零或更多列表pattern
模式必须至少包括一个参数包。
解释
可以用任意数量的模板参数实例化各种类模板:
二次
template<class ... Types> struct Tuple {};
Tuple<> t0; // Types contains no arguments
Tuple<int> t1; // Types contains one argument: int
Tuple<int, float> t2; // Types contains two arguments: int and float
Tuple<0> error; // error: 0 is not a type
二次
可以用任意数量的函数参数%28调用变量函数模板。模板参数推导29%:
二次
template<class ... Types> void f(Types ... args
f( // OK: args contains no arguments
f(1 // OK: args contains one argument: int
f(2, 1.0 // OK: args contains two arguments: int and double
二次
在主类模板中,模板参数包必须是模板参数列表中的最终参数。在函数模板中,模板参数包可能会出现在列表的前面,条件是可以从函数参数中推导出以下所有参数,或者具有默认参数:
二次
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
valid(1.0, 1, 2, 3 // OK: deduces U as double, Ts as {int,int,int}
二次
包装膨胀
后面跟着省略号的一种模式,其中至少一个参数包的名称至少出现一次。扩大
转换为模式的零个或多个逗号分隔的实例化,其中参数包的名称按顺序被包中的每个元素替换。
二次
template<class ...Us> void f(Us... pargs) {}
template<class ...Ts> void g(Ts... args) {
f(&args... // “&args...” is a pack expansion
// “&args” is its pattern
}
g(1, 0.2, "a" // Ts... args expand to int E1, double E2, const char* E3
// &args... expands to &E1, &E2, &E3
// Us... pargs expand to int* E1, double* E2, const char** E3
二次
如果两个参数包的名称以相同的模式出现,则它们同时展开,并且必须具有相同的长度:
二次
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 pack expansion
// Pair<Args1, Args2> is the pattern
};
};
typedef zip<short, int>::with<unsigned short, unsigned>::type T1;
// Pair<Args1, Args2>... expands to
// Pair<short, unsigned short>, Pair<int, unsigned int>
// T1 is Tuple<Pair<short, unsigned short>, Pair<int, unsigned>>
typedef zip<short>::with<unsigned short, unsigned>::type T2;
// error: pack expansion contains parameter packs of different lengths
二次
如果一个包展开嵌套在另一个包扩展中,则它将展开显示在最里面的包展开中的参数Pack,并且必须在封装包展开中提到另一个包,但在最内部的包中则不能这样做:
二次
template<class ...Args>
void g(Args... args) {
f(const_cast<const Args*>(&args)...
// const_cast<const Args*>(&args) is the pattern, it expands two packs
// (Args and args) simultaneously
f(h(args...) + args... // Nested pack expansion:
// 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(&args... // expands to f(&E1, &E2, &E3)
f(n, ++args... // expands to f(n, ++E1, ++E2, ++E3
f(++args..., n // expands to f(++E1, ++E2, ++E3, n
f(const_cast<const Args*>(&args)...
// f(const_cast<const E1*>(&X1), const_cast<const E2*>(&X2), const_cast<const E3*>(&X3))
f(h(args...) + args... // expands to
// f(h(E1,E2,E3) + E1, h(E1,E2,E3) + E2, h(E1,E2,E3) + E3)
二次
模板参数列表
可以在模板参数列表中的任何地方使用Pack展开,只要模板具有匹配展开的参数。
二次
template<class A, class B, class...C> void func(A arg1, B arg2, C...arg3)
{
container<A,B,C...> t1; // expands to container<A,B,E1,E2,E3>
container<C...,A,B> t2; // expands to container<E1,E2,E3,A,B>
container<A,C...,B> t3; // expands to container<A,E1,E2,E3,B>
}
二次
功能参数表
在函数参数列表中,如果省略号出现在参数声明%28中,它是否在参数包%28中指定函数参数包,args...
ARGS%29或非%29参数声明是模式:
二次
template<typename ...Ts> void f(Ts...) {}
f('a', 1 // Ts... expands to void f(char, int)
f(0.1 // Ts... expands to void f(double)
template<typename ...Ts, int... N> void g(Ts (&...arr)[N]) {}
int n[1];
g<const char, int>("a", n // Ts (&...arr)[N] expands to
// const char (&)[2], int(&)[1]
二次
注:在模式中Ts (&...arr)[N]
,省略号是最内部的元素,而不是所有其他包展开中的最后一个元素。
注:Ts (&...)[N]
不允许使用,因为C++11语法要求括号大小的省略号有一个名称:CWG#1488...
模板参数列表
Pack展开可能出现在模板参数列表中:
二次
template<typename... T> struct value_holder
{
template<T... Values> // expands to a non-type template parameter
struct apply { }; // list, such as <int, char, int(&)[5]>
};
二次
基说明符和成员初始化程序列表
包展开可以指定类声明通常,这也意味着构造函数需要在成员初始化列表调用这些基的构造函数:
二次
template<class... Mixins>
class X : public Mixins... {
public:
X(const Mixins&... mixins) : Mixins(mixins)... { }
};
二次
带括号的init列表
在大括号内列表%28大括号内的初始化程序列表和其他大括号-init-列表中,用于列表初始化在其他一些上下文中,还可能出现包扩展:
二次
template<typename... Ts> void func(Ts... args){
const int size = sizeof...(args) + 2;
int res[size] = {1,args...,2};
// since initializer lists guarantee sequencing, this can be used to
// call a function on each element of a pack, in order:
int dummy[sizeof...(Ts)] = { (std::cout << args, 0)... };
}
二次
兰伯达
参数包可能出现在兰卜达表情。
二次
template<class ...Args>
void f(Args... args) {
auto lm = [&, args...] { return g(args... };
lm(
}
二次
规模...操作员
大相当大的..。操作员也被归类为包扩展。
二次
template<class... Types>
struct count {
static const std::size_t value = sizeof...(Types
};
二次
动态异常规范
类中的异常列表。动态异常规范也可能是包的扩展。
二次
template<class...X> void func(int arg) throw(X...)
{
// ... throw different Xs in different situations
}
二次
对齐说明符
在类型列表和关键字使用的表达式列表中都允许进行包扩展。对齐...
属性列表
的列表中允许进行包扩展。属性,如[[attributes...]]
例如:void [[attributes...]] function()
...
Fold-expressions In fold-expressions, the pattern is the entire subexpression that does not contain an unexpanded parameter pack. Using-declarations In using declaration, ellipsis may appear in the list of declarators, this is useful when deriving from a parameter pack: template | (since C++17) |
---|
注记
例
二次
#include <iostream>
void tprintf(const char* format) // base function
{
std::cout << format;
}
template<typename T, typename... Targs>
void tprintf(const char* format, T value, Targs... Fargs) // recursive variadic function
{
for ( ; *format != '\0'; format++ ) {
if ( *format == '%' ) {
std::cout << value;
tprintf(format+1, Fargs... // recursive call
return;
}
std::cout << *format;
}
}
int main()
{
tprintf("% world% %\n","Hello",'!',123
return 0;
}
二次
产出:
二次
Hello world! 123
二次
上面的示例定义了一个类似于std::printf
,它用值替换格式字符串中字符%的每次出现。
当只传递格式字符串且没有参数展开时,将调用第一个重载。
第二个重载包含参数头和参数包的单独模板参数,这允许递归调用只传递参数的尾部,直到参数变为空。
Targs
是模板参数包,并且Fargs
函数参数包。
另见
功能模板
*。
类模板
查询参数包中的元素数。
C型变分函数
预处理宏也可以是可变的。
© cppreference.com
在CreativeCommonsAttribution下授权-ShareAlike未移植许可v3.0。