Parameter pack
A template parameter pack is a template parameter that accepts zero or more template arguments (non-types, types, or templates). A function parameter pack is a function parameter that accepts zero or more function arguments.
A template with at least one parameter pack is called a variadic template.
Syntax
Template parameter pack (appears in a and in a parameter list)
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) | |||||||
Function parameter pack (a form of , appears in a function parameter list of a variadic function template)
Args ... args(optional) | (4) | (since C++11) | |||||||
Parameter pack expansion (appears in a body of a variadic template)
pattern ... | (5) | (since C++11) | |||||||
pattern
s. Pattern must include at least one parameter pack. Explanation
A variadic class template can be instantiated with any number of template arguments:
templatestruct Tuple { }; Tuple<> t0; // Types contains no arguments Tuple t1; // Types contains one argument: int Tuple t2; // Types contains two arguments: int and float Tuple<0> error; // error: 0 is not a type
A variadic function template can be called with any number of function arguments (the template arguments are deduced through ):
templatevoid 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
In a primary class template, the template parameter pack must be the final parameter in the template parameter list. In a function template, the template parameter pack may appear earlier in the list provided that all following parameters can be deduced from the function arguments, or have default arguments:
templatestruct Invalid; // Ts.. not at the end template void invalid(); // U not deduced //U deduced, anonymous parameter defaulted template void valid(Ts..., U);
Pack expansion
A pattern followed by an ellipsis, in which the name of at least one parameter pack appears at least once, is expanded into zero or more comma-separated instantiations of the pattern, where the name of the parameter pack is replaced by each of the elements from the pack, in order.
templatevoid f(Us... pargs) { } template 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
If the names of two parameter packs appear in the same pattern, they are expanded simultaneously, and they must have the same length:
templatestruct Tuple { }; template struct Pair { }; template struct zip { template struct with { typedef Tuple ...> type; // Pair ... is the pack expansion // Pair is the pattern }; }; typedef zip ::with ::type T1; // Pair ... expands to // Pair , Pair // T1 is Tuple , Pair > typedef zip ::with ::type T2; // error: pack expansion contains parameter packs of different lengths
If a pack expansion is nested within another pack expansion, the parameter packs that appear inside the innermost pack expansion are expanded by it, and there must be another pack mentioned in the enclosing pack expansion, but not in the innermost one:
templatevoid g(Args... args) { f(const_cast (&args)...); // const_cast (&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) }
Expansion loci
Depending on where the expansion takes place, the resulting comma-separated list is a different kind of list: function parameter list, member initializer list, attribute list, etc. The following is the list of all allowed contexts
Function argument lists
A pack expansion may appear inside the parentheses of a function call operator, in which case the largest expression to the left of the ellipsis is the pattern that is expanded.
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(&args)...); // f(const_cast (&X1), const_cast (&X2), const_cast (&X3)) f(h(args...) + args...); // expands to // f(h(E1,E2,E3) + E1, h(E1,E2,E3) + E2, h(E1,E2,E3) + E3)
Template argument lists
Pack expansions can be used anywhere in a template argument list, provided the template has the parameters to match the expansion.
Function parameter list
In a function parameter list, if an ellipsis appears in a parameter declaration (whether it names a function parameter pack (as in, Args ...
args) or not) the parameter declaration is the pattern:
templatevoid f(Ts...) { } f('a', 1); // Ts... expands to void f(char, int) f(0.1); // Ts... expands to void f(double) template void g(Ts (&...arr)[N]) { } int n[1]; g ("a", n); // Ts (&...arr)[N] expands to // const char (&)[2], int(&)[1]
Note: In the pattern Ts (&...arr)[N]
, the ellipsis is the innermost element, not the last element as in all other pack expansions.
Note: Ts (&...)[N]
is not allowed because the C++11 grammar requires the parenthesized ellipsis to have a name: .
Template parameter list
Pack expansion may appear in a template parameter list:
templatestruct value_holder { template // expands to a non-type template parameter struct apply { }; // list, such as };
Base specifiers and member initializer lists
A pack expansion may designate the list of base classes in a . Typically, this also means that the constructor needs to use a pack expansion in the to call the constructors of these bases:
templateclass X : public Mixins... { public: X(const Mixins&... mixins) : Mixins(mixins)... { } };
Braced init lists
In a braced-init-list (brace-enclosed list of initializers and other braced-init-lists, used in and some other contexts), a pack expansion may appear as well:
templatevoid 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)] = { ( << args, 0)... }; }
Lambda captures
A parameter pack may appear in the capture clause of a expression
templatevoid f(Args... args) { auto lm = [&, args...] { return g(args...); }; lm(); }
The sizeof... operator
The operator is classified as a pack expansion as well
templatestruct count { static const value = sizeof...(Types); };
Dynamic exception specifications
The list of exceptions in a may also be a pack expansion
templatevoid func(int arg) throw(X...) { // ... throw different Xs in different situations }
Alignment specifier
Pack expansions are allowed in both the lists of types and the lists of expressions used by the keyword
Attribute list
Pack expansions are allowed in the lists of , as in [[attributes...]]. For example: void [[attributes...]] function()
Fold-expressionsIn , the pattern is the entire subexpression that does not contain an unexpanded parameter pack. Using-declarationsIn , ellipsis may appear in the list of declarators, this is useful when deriving from a parameter pack: template | (since C++17) |
Notes
This section is incompleteReason: a few words about partial specializations and other ways to access individual elements? Mention recursion vs logarithmic vs shortcuts such as fold expressions |
Example
#includevoid tprintf(const char* format) // base function { << format; } template void tprintf(const char* format, T value, Targs... Fargs) // recursive variadic function { for ( ; *format != '\0'; format++ ) { if ( *format == '%' ) { << value; tprintf(format+1, Fargs...); // recursive call return; } << *format; } } int main() { tprintf("% world% %\n","Hello",'!',123); return 0; }
Output:
Hello world! 123
The above example defines a function similar to , that replace each occurrence of the character % in the format string with a value.
The first overload is called when only the format string is passed and there is no parameter expansion.
The second overload contains a separate template parameter for the head of the arguments and a parameter pack, this allows the recursive call to pass only the tail of the parameters until it becomes empty.
Targs
is the template parameter pack and Fargs
is the function parameter pack
See also
Queries the number of elements in a parameter pack. | |
Can be variadic as well |