I'm curious about the following specific context: referring to a member type 
(supposedly, an injected class name) through a dependent type name with a 
`typename` keyword

  struct S {};

  template <typename T> void foo() {
    typename T::S s; // <-- this
  }

  int main() {
    foo<S>();
  }

GCC quietly accepts the declaration inside `foo` (even with `-pedantic`), i.e. 
it treats `T::S` as a reference to the injected class name. Clang issues the 
following diagnostic 

  warning: ISO C++ specifies that qualified reference to 'S' is a constructor 
    name rather than a type in this context, despite preceding 'typename' 
keyword 
    [-Winjected-class-name]

which expectedly turns into an error in `-pedantic-errors` mode.

Is Clang correct in this case? (Considering how elaborate and specific its 
message is.)

Note that the dilemma arises only when the actual argument is a type named `S`. 
If a differently named type is passed as `T` (which just happens to have `S` as 
a member type), both compilers accept the code (see the example below). This 
distinction is what tempted me to test this context in the first place, and 
what 
leads me to believe that Clang's behavior is "weird" (even if correct).

The difference can be converted to something more tangible by exploiting it for 
SFINAE purposes, e.g. 

  #include <iostream>

  struct A { using B = int; };
  struct B {};
  struct C {};

  template <typename T> void foo(int, typename T::B * = 0) {
    std::cout << "Hello World" << std::endl;
  }

  template <typename T> void foo(...) {
    std::cout << "Goodbye World" << std::endl;
  }

  int main() {
    foo<A>(0);
    foo<B>(0); // GCC vs Clang?
    foo<C>(0);
  }

In GCC this program outputs 

  Hello World
  Hello World
  Goodbye World

(https://godbolt.org/z/rjGhjzo8q). 

And in Clang it outputs

  Hello World
  Goodbye World
  Goodbye World

(https://godbolt.org/z/PejjP6h6K)

-- 
Best regards,
Andrey

Reply via email to