All standard references below refers to N4659: March 2017 post-Kona working draft/C++17 DIS.
Consider the following snippet:
#include <type_traits>
template <int N> struct num {};
template <typename> struct A;
// (1)
template <int N> struct A<num<N>> { using type = bool; };
// (2)
template <long N> struct A<num<N>> { using type = char; };
static_assert(!std::is_same_v<long, int>, "");
// (A)
static_assert(std::is_same_v<A<num<1>>::type, bool>, "");
int main() {}
The static_assert
at (A) is successful for GCC, but fails for Clang:
error: static_assert failed due to requirement 'std::is_same_v<char, bool>' ""
Essentially, GCC picks the perfectly matching specialization (1), whereas Clang picks the specialization (2).
Similarly, if we remove the assertions as well as specialization (1):
template <int N> struct num {};
template <typename> struct A;
// (2)
template <long N> struct A<num<N>> { using type = char; };
int main() {
A<num<1>> a{};
(void)a;
}
Then GCC fails to compile the program whereas Clang accepts it.
GCC:
error: variable '`A<num<1> > a`' has initializer but incomplete type
This behaviour holds over various GCC and Clang versions, as well as various C++ language levels over these version (C++11, C++14, C++17, C++2a).
My guess is that this is ill-formed, but haven't been able to apply a relevant part of [temp.class.spec] to reject it. Perhaps [temp.class.spec]/8.1?
[temp.class.spec]/8.1 The type of a template parameter corresponding to a specialized non-type argument shall not be dependent on a parameter of the specialization. [ Example: [...] — end example ]
As far as I can tell, the first snippet is ill-formed (and a diagnostic is required); compilers should reject the program because of the partial specialization (2).
[temp.deduct.type]/18 applies here:
If
P
has a form that contains<i>
, and if the type ofi
differs from the type of the corresponding template parameter of the template named by the enclosing simple-template-id, deduction fails. [...]
The associated example in the Standard uses a function template, but is otherwise very similar.
So the template argument of the partial specialization (2) can never be deduced, and [temp.class.spec.match]/3 applies:
If the template arguments of a partial specialization cannot be deduced because of the structure of its template-parameter-list and the template-id, the program is ill-formed.
Interestingly, I couldn't find a compiler that diagnoses this issue, not even EDG in strict mode. We could speculate that most compiler writers consider the benefits of having a diagnostic here not to be worth the effort of implementing the checks. This could mean that we might see the requirement in the paragraph above change in the future from ill-formed to ill-formed, no diagnostic required. However, this is pure speculation. In any case, I don't see it ever changing to well-formed; I can't think of a valid use for a partial specialization that never matches.
The wording of [temp.deduct.type]/18 was clarified by the resolution of CWG2091.
The standard is not nearly precise enough about the template argument deduction for a partial specialization ([temp.class.spec.match]/2) to definitively determine the meaning of your example. In particular, all deduction is ultimately defined in terms of types ([temp.deduct.type]), but there are no types involved for a non-type template argument.
The deduction for partial ordering among partial specializations handles this case in terms of an invented class template ([temp.class.order]/1.2), which brings to bear the rule that deduction fails for any mismatch between the type of a non-type template argument and its parameter ([temp.deduct.type]/18). That makes any use of A<num<…>>
in your example ambiguous if both partial specializations match (avoiding the need to determine whether a narrowing conversion was involved in using the “unique value” synthesized for partial ordering ([temp.func.order]/3) as a template argument). However, if we apply the same rule to the matching itself, we find (as does GCC) that (2) never matches. In turn, that arguably should provoke a diagnostic for the specialization itself ([temp.class.spec.match]/3, as bogdan’s answer mentioned), although it’s not entirely obvious what “structure” there is meant to include if the error is to be diagnosable and no compiler rejects it.
Meanwhile, [temp.class.spec]/8.1 is certainly irrelevant: there are no specialized non-type arguments involved.
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