Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Variadic template argument order, must they always be the right most argument?

I would like to modify an existing class constructor:

template< typename T, typename... Ts >
MyClass( std::vector<T>& head, Ts& ...tail );

So that a processing flag can be specified:

template< typename T, typename... Ts >
MyClass( MyEnum myEnum, std::vector<T>& head, Ts& ...tail );

This works fine, however I was wondering if there is a way for it to be specified as the right-most argument, and possibly with a default value. I've never seen variadic templates declared like that, but then again, I can't find anything explicitly stating that they can't be. I tried:

template< typename T, typename... Ts >
MyClass( std::vector<T>& head, Ts& ...tail, MyEnum myEnum );

...

MyClass myClass( dataA, dataB, dataC, MyEnum::VALUE );

But the compiler does not like it, I'm assuming it is due to how variadic templates are resolved and that they must be the right-most parameter?

Is this possible in C++11?

like image 528
kbirk Avatar asked Aug 15 '14 02:08

kbirk


People also ask

What is Variadic template in C++?

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. However, variadic templates help to overcome this issue.

How do you call a variadic function?

You don't have to do anything special to call a variadic function. Just put the arguments (required arguments, followed by optional ones) inside parentheses, separated by commas, as usual. But you must declare the function with a prototype and know how the argument values are converted.


2 Answers

It's not illegal to have a function parameter pack in the middle, but you run into a gigantic hurdle when you try to do this with a constructor: a function parameter pack that does not occur at the end of the parameter-declaration-list is a non-deduced context (§14.8.2.5 [temp.deduct.type]/p5, last bullet point), and constructor templates must be called using template argument deduction - you can't explicit specify their template parameters.

That is, to have a function parameter pack in the middle, you must explicitly specify the template parameters when you call the function:

template< typename T, typename... Ts >
void test( const std::vector<T>& head, const Ts& ...tail, MyEnum myEnum ) { }

test<double, int>(std::vector<double>(), 10, MyEnum()); // legal

But this will not compile:

test(std::vector<double>(), 10, MyEnum()); // No deduction performed for the parameter pack

There is, however, no way in the language to explicitly specify the template parameters of a constructor template when you call it (§14.8.1 [temp.arg.explicit]/p7):

[ Note: Because the explicit template argument list follows the function template name, and because conversion member function templates and constructor member function templates are called without using a function name, there is no way to provide an explicit template argument list for these function templates. —end note ]

Thus, for constructors, you have to rely on template argument deduction - which doesn't work if your function parameter pack is not at the end.

like image 81
T.C. Avatar answered Sep 29 '22 16:09

T.C.


You can "wrap" the variadic in an std::tuple:

template< typename T, typename... Ts >
MyClass( std::vector<T>& head, std::tuple<Ts& ...> tail, MyEnum myEnum );

...

MyClass myClass( dataA, std::tie( dataB, dataC ), MyEnum::VALUE );
// NOTE:                ^^^^^^^^^              ^

Live example (rev 2)

like image 28
Mark Garcia Avatar answered Sep 29 '22 14:09

Mark Garcia