Injected class name as type

  • A+
Category:Languages

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 of C ([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.

Comment

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen: