Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Parameter pack must be at the end of the parameter list... When and why?

I don't get the reason for which a parameter pack must be at the end of the parameter list if the latter is bound to a class, while the constraint is relaxed if the parameter list is part of a member method declaration.

In other terms, this one compiles:

class C {     template<typename T, typename... Args, typename S>     void fn() { } }; 

The following one does not:

template<typename T, typename... Args, typename S> class C { }; 

Why is the first case considered right and the second one is not?
I mean, if it's legal syntax, shouldn't it be in both the cases?

To be clear, the real problem is that I was defining a class similar to the following one:

template<typename T, typename... Args, typename Allocator> class C { }; 

Having the allocator type as the last type would be appreciated, but I can work around it somehow (anyway, if you have a suggestion it's appreciated, maybe yours are far more elegant than mine!!).
That said, I got the error:

parameter pack 'Args' must be at the end of the template parameter list

So, I was just curious to fully understand why it's accepted in some cases, but it is not in some others.

Here is a similar question, but it simply explains how to solve the problem and that was quite clear to me.

like image 952
skypjack Avatar asked Jan 22 '16 07:01

skypjack


People also ask

What is a parameter pack?

[edit] A template parameter pack is a template parameter that accepts zero or more template arguments (non-types, types, or templates). A function parameter pack is a function parameter that accepts zero or more function arguments. A template with at least one parameter pack is called a variadic template.

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.

What is Pack expansion?

Pack expansion (C++11) A pack expansion is an expression that contains one or more parameter packs followed by an ellipsis to indicate that the parameter packs are expanded. Consider the following example: template<class...T> void func(T...a){}; template<class...U> void func1(U...b){ func(b...


2 Answers

It is valid for function templates but only when argument deduction can help the compiler resolve the template parameters, as it stands your function template example is virtually useless because

template<typename T, typename... Args, typename S> void fn() { } int main() { fn<int, int, int>(); } 
test.cpp: In function 'int main()': test.cpp:2:32: error: no matching function for call to 'fn()'  int main() { fn<int, int, int>(); }                                 ^ test.cpp:1:57: note: candidate: template<class T, class ... Args, class S> void fn()  template<typename T, typename... Args, typename S> void fn() { }                                                          ^ test.cpp:1:57: note:   template argument deduction/substitution failed: test.cpp:2:32: note:   couldn't deduce template parameter 'S'  int main() { fn<int, int, int>(); } 

the compiler has no way of determining which template parameters belong to the parameter pack, and which to S. In fact as @T.C. points out it should actually be a syntax error because a function template defined in this manner cannot ever be instantiated.

A more useful function template would be something like

template<typename T, typename... Args, typename S> void fn(S s) { } 

as now the compiler is able to unambiguously match the function parameter s with the template type S, with the side effect that S will always be deduced - all explicit template parameters after the first will belong to Args.

None of this works for (primary) class templates, parameters aren't deduced and it's expressly forbidden:

From draft n4567

http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2015/n4567.pdf

[temp.param] / 11

[...]If a template-parameter of a primary class template or alias template is a template parameter pack, it shall be the last template-parameter.[...]

(if they were deduced it would be ambiguous as in the function template example).

like image 124
user657267 Avatar answered Sep 22 '22 08:09

user657267


The first one is not right. The compiler is just buggy and failed to diagnose it. [temp.param]/11:

A template parameter pack of a function template shall not be followed by another template parameter unless that template parameter can be deduced from the parameter-type-list of the function template or has a default argument (14.8.2).


If the function type T(Args...) is meaningful to the end-user, one way to fix this would be to use a partial specialization instead:

template<class F, class Alloc> class C; //undefined template<class T, class... Args, class Alloc> class C<T(Args...), Alloc> {     // implementation }; 

Depending on the actual requirements, type-erasing the allocator might also be worth considering.

like image 20
T.C. Avatar answered Sep 20 '22 08:09

T.C.