在线文档教程
C++
语言 | Language

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 struct X : bases... { using bases::g...; }; X x; // OK: B::g and D::g introduced(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。

http://en.cppreference.com/w/cpp/language/contameter[医]包