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.
[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.
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.
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...
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).
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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With