In C++11 there are variadic templates like this one:
template< class T, class... Args > unique_ptr<T> make_unique( Args&&... args ) { return unique_ptr<T>(new T(std::forward<Args>(args)...)); }
There are some curiosities about this: The expression std::forward<Args>(args)...
uses both Args
and args
but only one ...
token. Furthermore std::forward
is a non-variadic template function taking only one template parameter and one argument. What are the syntax rules for that (roughly)? How can it be generalized?
Also: In the function implementation the ellipsis (...
) is at the end of the expression of interest. Is there a reason that in the template argument list and the parameter list the ellipsis is in the middle?
With the variadic templates feature, you can define class or function templates that have any number (including zero) of parameters. To achieve this goal, this feature introduces a kind of parameter called parameter pack to represent a list of zero or more parameters for templates.
Variadic templates are class or function templates, that can take any variable(zero or more) number of arguments. In C++, templates can have a fixed number of parameters only that have to be specified at the time of declaration.
Parameter packs (C++11) A parameter pack can be a type of parameter for templates. Unlike previous parameters, which can only bind to a single argument, a parameter pack can pack multiple parameters into a single parameter by placing an ellipsis to the left of the parameter name.
In the context of variadic template, the ellipsis ...
is used to unpack the template parameter pack if it appears on the right side of an expression (call this expression pattern for a moment), or it's a pack argument if it appears on left side of the name:
...thing // pack : appears as template arguments thing... // unpack : appears when consuming the arguments
The rule is that whatever pattern is on the left side of ...
is repeated — the unpacked patterns (call them expressions now) are separated by comma ,
.
It can be best understood by some examples. Suppose you have this function template:
template<typename ...T> //pack void f(T ... args) //pack { // here are unpack patterns g( args... ); //pattern = args h( x(args)... ); //pattern = x(args) m( y(args...) ); //pattern = args (as argument to y()) n( z<T>(args)... ); //pattern = z<T>(args) }
Now if I call this function passing T
as {int, char, short}
, then each of the function call is expanded as:
g( arg0, arg1, arg2 ); h( x(arg0), x(arg1), x(arg2) ); m( y(arg0, arg1, arg2) ); n( z<int>(arg0), z<char>(arg1), z<short>(arg2) );
In the code you posted, std::forward
follows the fourth pattern illustrated by n()
function call.
Note the difference between x(args)...
and y(args...)
above!
You can use ...
to initialize an array also as:
struct data_info { boost::any data; std::size_t type_size; }; std::vector<data_info> v{{args, sizeof(T)}...}; //pattern = {args, sizeof(T)}
which is expanded to this:
std::vector<data_info> v { {arg0, sizeof(int)}, {arg1, sizeof(char)}, {arg2, sizeof(short)} };
I just realized a pattern could even include access specifier such as public
, as shown in the following example:
template<typename ... Mixins> struct mixture : public Mixins ... //pattern = public Mixins { //code };
In this example, the pattern is expanded as:
struct mixture__instantiated : public Mixin0, public Mixin1, .. public MixinN
That is, mixture
derives publicly from all the base classes.
Hope that helps.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With