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:
- it unpacks 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.
...
comes before the identifier being introduced. When using either or both types of packs, the...
comes after the expression pattern to expand. – Davy