A while ago I answered a stackoverflow question related to C++11’s variadic templates. I gave an example of one of the simplest variadic template functions imaginable:

int maximum ( int n ) { return n ; } template < typename . . . Args > int maximum ( int n , Args . . . rest ) { return max ( n , maximum ( rest . . . ) ) ; }

A commenter asked if the ellipsis after Args and rest have any meaning or are they just syntactic salt?

To get the jargon straight Args is the variadic template pattern, rest is the variadic parameter pack and the act of turning a template pack into parameters is called pack expansion.

This is a good question, the trailing ellipsis are used to expand the template parameter pack but since the only thing[1] you can do with a parameter pack (or pattern) is expand it, why do we need the extra syntax?

I didn’t know the answer at first but after a bit of thought I believe this is the rationale:

When you unpack a template pack the compiler conceptually writes out the expanded pack, so if rest is { -2, 3, -4 } the compiler turns maximum(rest...); into maximum(-2, 3, -4); but you can do more, if you want to find the maximum of the absolute value of the numbers you could write maximum(abs(rest)...) (note the ellipsis come after the close paren) which would expand to maximum(abs(-2), abs(3), abs(-4)) . The location of the ellipsis matters that’s why you have to write it explicitly.

This is also true with the template patterns.

template < typename . . . Pack > struct united : std :: tuple < Pack . . . > { } ; united < const char * , int , bool > we_stand ; // we_stand is equivalent to being of type: // struct united_XYZ : std::tuple {}; std :: tuple < const char * , int , bool > * ok = & we_stand ; std :: tuple < bool > * error = & we_stand ; template < typename . . . Pack > struct divided : std :: tuple < Pack > . . . { } ; divided < const char * , int , bool > we_fall ; // we_fall is equivalent to being of type: // struct divided_XYZ : std::tuple, std::tuple, std::tuple {}; std :: tuple < bool > * ok = & we_fall ; std :: tuple < const char * , int , bool > * error = & we_fall ;



[1] And call sizeof… on it but that’s just nitpicking. ↩