Reputation: 37606
The following code:
#include <cstddef>
template <size_t N,
typename T,
T first,
T... rest>
struct A {
};
template<typename T,
T... args>
struct A<0, T, args...> {
};
int main () {
A<0, int, 1> a0;
A<2, int, 1, 2> a2;
return 0;
}
...does not compile with g++
(version 5.1.0
and 5.3.0
) due to:
error: partial specialization is not more specialized than the primary template because it replaces multiple parameters with a pack expansion
...but compiles with clang
.
Is it allowed to declare such partial specializations?
Side note: Actually, the specialization is dangerous since A<0, int>
fails to compile with both compiler (wrong number of template arguments).
Upvotes: 9
Views: 476
Reputation: 7788
consider:
template <class T, T first, T... rest>
struct X{};
template<class T, T... rest>
struct X<T, rest...>{};
Exact same error.
Since first
can be interpreted as first element of the rest
pack, it makes no difference in such specialization. If you add first
in the specialization - well, that's the primary template. In your specialization though you can add first
:
//specialization
template<typename T,
T first,
T... args>
struct A<0, T, first, args...> {
};
Upvotes: 0
Reputation: 302663
gcc is correct, the code is ill-formed because the specialization isn't actually more specialized.
The rule, from [temp.class.spec] is (as a result of DR 1495, h/t T.C. for the link):
Within the argument list of a class template partial specialization, the following restrictions apply: [...] The specialization shall be more specialized than the primary template (14.5.5.2).
In order to determine that, we would rewrite the two as synthesized function templates:
template <size_t N, class T, T first, T... rest>
void __f(A<N, T, first, rest...> ); // primary
template <class T, T... args>
void __f(A<0, T, args...> ); // specialization
and then go through the partial ordering rules. That, in turn, involves synthesizing new types/values for each of the template parameters and seeing if deduction can succeed in either direction.
Definitely, the deduction of the specialization fails with the primary (due to N
vs 0
). In the other direction, from [temp.deduct.partial]:
If
A
was transformed from a function parameter pack andP
is not a parameter pack, type deduction fails.
Since we're trying to deduce T first
against a pack, deduction fails in this direction as well. This means that neither of the synthesized function templates is more specialized than other, which in turn means that the class template specialization is not more specialized than the primary template. Hence, gcc would be correct to reject.
Upvotes: 9