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?
In the context of variadic template, the ellipsis ...
is used to either pack or unpack parameters/arguments in a template definition, depending on the relative position where it appears:
...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.