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).
Question
- Is the first snippet above in fact ill-formed (no diagnostic required?), or is either GCC or Clang wrong?
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 ]