Given the following code,
template <class> using void_t = void;
template <class C, class = void> struct X { enum { v = 0 }; };
template <class C> struct X<C, void_t<typename C::T> > { enum { v = 1 }; };
struct T { };
int main() { return X<T>::v; }
what should main return? GCC and MSVC say 1, Clang says 0.
I think Clang is right here. The rule in [class.qual] is:
In a lookup in which function names are not ignored and the nested-name-specifier nominates a class
C
:
- if the name specified after the nested-name-specifier, when looked up in
C
, is the injected-class-name ofC
([class]), or- [... irrelevant here ...]
the name is instead considered to name the constructor of class
C
. [ Note: For example, the constructor is not an acceptable lookup result in an elaborated-type-specifier so the constructor would not be used in place of the injected-class-name. — end note ] Such a constructor name shall be used only in the declarator-id of a declaration that names a constructor or in a using-declaration. [ Example:struct A { A(); }; struct B: public A { B(); }; A::A() { } B::B() { } B::A ba; // object of type A A::A a; // error, A::A is not a type name struct A::A a2; // object of type A
— end example ]
typename C::T
is the same kind of thing as A::A
, it's lookup in which function names are not ignored (typename
doesn't cause function names to be ignored). So, in typename C::T
, when C
is T
, the name T
is considered to name the constructor. As it's not a type name, we should get a substitution failure and fallback to the primary template.
Filed 86818.
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