kbirk
kbirk

Reputation: 4022

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?

Upvotes: 16

Views: 2274

Answers (2)

T.C.
T.C.

Reputation: 137425

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.

Upvotes: 22

Mark Garcia
Mark Garcia

Reputation: 17708

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)

Upvotes: 9

Related Questions