Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Injected class name as type

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.

like image 669
Hyman Rosen Avatar asked Jul 31 '18 20:07

Hyman Rosen


1 Answers

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.

like image 146
Barry Avatar answered Sep 24 '22 03:09

Barry