skypjack
skypjack

Reputation: 50550

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.

Upvotes: 28

Views: 7199

Answers (2)

user657267
user657267

Reputation: 21000

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).

Upvotes: 17

T.C.
T.C.

Reputation: 137315

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.

Upvotes: 12

Related Questions