Search code examples
c++language-lawyerstatic-methodssfinaename-lookup

Why class::class::class::staticClassMember() compiles (in C++)?


I must have missed something in C++ specification because I can't explain why the following code compiles successfully:

class MyClass { static void fun(); };
int main() { MyClass::MyClass::MyClass::fun(); }

Could somebody point me to the standard or just explain me the semantics? I would guess that only one MyClass:: is allowed. Two MyClass::MyClass:: should cause error. Experimenting with MS Visual C++ 2017 and GNU C++ 6.2.0 I realized that any count of MyClass:: is allowed.

It is not only a theoretical question. I wanted to use SFINAE and condition compilation with existence of a sub-class. Worked good until the base class has the same name as the sub-class:

template <class T> void callWorkout() { T::SubClass::workout(); }
struct X { struct SubClass { static void workout(); }; };
struct Y { /*empty*/ };
struct SubClass { static void workout(); };

int main() {
  callWorkout<X>();  // works fine - compiled
  callWorkout<Y>();  // works "fine" - not compiled, no SubClass in Y
  callWorkout<SubClass>();  // ooops? - still compiled, there is no 'SubClass' in SubClass
}

My question has two parts:

  • What is the exact semantics of MyClass::MyClass::?
  • How can I fix the above example not to compile callWorkout<SubClass>()? (I tried to add sizeof(typename T::SubClass) but surprisingly it compiles also for T=SubClass)

Solution

  • That's the injected class name of MyClass. And you can verify it's not T by simply using std::is_same_v<T, typename T::SubClass> in a SFINAE conditional.

    template <class T>
    auto callWorkout() -> std::enable_if_t<!std::is_same_v<T, typename T::SubClass>>
    { T::SubClass::workout(); }
    

    If you don't need SFINAE (because you aren't trying to control overload resolution), then a static_assert with a descriptive custom message can also do nicely.