While debugging someone's nasty macro-generated code, I saw that MSVC++ alone was not happy with something akin to the following function template declaration:
template <typename ...Vs, typename>
void foo();
Fair enough. I was puzzled though as to why GCC and Clang would compile that. I added a definition of foo
to the declaration above, and now GCC also produces a compilation error (Clang remains content). This code is below:
template <typename ...Vs, typename>
void foo();
template <typename ...Vs, typename = int>
void foo() { }
int main(int argc, char *argv[])
{
foo<char,float>();
return 0;
}
Is Clang right or wrong? I note that GCC and MSVC++ can compile if the declaration is removed.
Which parameter is legal for non-type template? Explanation: The following are legal for non-type template parameters:integral or enumeration type, Pointer to object or pointer to function, Reference to object or reference to function, Pointer to member.
A non-type template argument provided within a template argument list is an expression whose value can be determined at compile time. Such arguments must be constant expressions, addresses of functions or objects with external linkage, or addresses of static class members.
A template argument for a template template parameter is the name of a class template. When the compiler tries to find a template to match the template template argument, it only considers primary class templates. (A primary template is the template that is being specialized.)
A template parameter is a special kind of parameter that can be used to pass a type as argument: just like regular function parameters can be used to pass values to a function, template parameters allow to pass also types to a function.
Clang is correct. The template parameter pack may appear earlier if the following parameters have default arguments.
In a primary class template, the template parameter pack must be the final parameter in the template parameter list. In a function template, the template parameter pack may appear earlier in the list provided that all following parameters can be deduced from the function arguments, or have default arguments:
template<typename... Ts, typename U> struct Invalid; // Error: Ts.. not at the end template<typename ...Ts, typename U, typename=void> void valid(U, Ts...); // OK: can deduce U // void valid(Ts..., U); // Can't be used: Ts... is a non-deduced context in this position valid(1.0, 1, 2, 3); // OK: deduces U as double, Ts as {int,int,int}
Then given foo<char,float>();
, Vs
is deduced as char, float
, and the 2nd template argument would be int
.
From the standard, [temp.param]/13
If a template-parameter of a primary class template, primary variable template, or alias template is a template parameter pack, it shall be the last template-parameter. 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 ([dcl.fct]) of the function template or has a default argument ([temp.deduct]).
And about the behavior of gcc and msvc,
GCC and MSVC++ will successfully compile if the declaration is removed.
Gcc and MSVC seem failing to merge the default template arguments, which appear in the declarations and the definition should be merged.
Default template arguments that appear in the declarations and the definition are merged similarly to default function arguments:
template<typename T1, typename T2 = int> class A; template<typename T1 = int, typename T2> class A; // the above is the same as the following: template<typename T1 = int, typename T2 = int> class A;
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