Junekey Jeon
Junekey Jeon

Reputation: 1577

Partial specialization of nested class on enum defined inside a template class

Please see the following code:

template <class T>
struct X {
  enum class E { e0 };

  template <class U, E e>
  struct Y {};

  template <class U>
  struct Y<U, E::e0> {
    static int f() { return 0; }
  };
};

int main() {
  X<int>::Y<int, X<int>::E::e0>::f();
}

VC++ 15.7.5 generates an error message:

1>  test.cpp
1>  some_directory\test.cpp(15): error C2039: 'f': is not a member of 'X<int>::Y<int,X<int>::E::e0>'
1>  some_directory\test.cpp(15): note: see declaration of 'X<int>::Y<int,X<int>::E::e0>'
1>  some_directory\test.cpp(15): error C3861: 'f': identifier not found

However, GCC & Clang seem to accept this code.

Which one is correct?

EDIT

On older version of VC++ generates the following error message:

source_file.cpp(9): error C2754: 'X<T>::Y<U,>': a partial specialization cannot have a dependent non-type template parameter
source_file.cpp(12): note: see reference to class template instantiation 'X<T>' being compiled
source_file.cpp(16): error C2039: 'f': is not a member of 'X<int>::Y<int,X<int>::E::e0>'
source_file.cpp(16): error C3861: 'f': identifier not found

So I think the reason VC++ refuses to compile is that enum class E is defined inside a template class. Indeed, the error disappears (both in the old version and the recent 15.7.5) if I move enum class E outside X.

Is this case really a case of partial specialization on a dependent non-type template parameter?

Upvotes: 4

Views: 473

Answers (1)

GCC and Clang are correct to accept it. This is all according to [temp.class.spec]/5, which also has a supporting example:

A class template partial specialization may be declared in any scope in which the corresponding primary template may be defined ([namespace.memdef], [class.mem], [temp.mem]). [ Example:

template<class T> struct A {
  struct C {
    template<class T2> struct B { };
    template<class T2> struct B<T2**> { };      // partial specialization #1
  };
};

// partial specialization of A<T>​::​C​::​B<T2>
template<class T> template<class T2>
  struct A<T>::C::B<T2*> { };                   // #2

A<short>::C::B<int*> absip;                     // uses partial specialization #2

 — end example ]

A workaround with MSVC may be to try and specialize the member template at namespace scope.


As for your edit, I would say MSVC is still wrong. The relevant standardese is at [temp.class.spec]/8:

Within the argument list of a class template partial specialization, the following restrictions apply:

  • The type of a template parameter corresponding to a specialized non-type argument shall not be dependent on a parameter of the specialization. [ Example:

    template <class T, T t> struct C {};
    template <class T> struct C<T, 1>;              // error
    
    template< int X, int (*array_ptr)[X] > class A {};
    int array[5];
    template< int X > class A<X,&array> { };        // error
    

     — end example ]

Applying it to your example in struct Y<U, E::e0>: does the type of the enumeration depend on another template argument to Y? The answer is no. It certainly depends on X<T>, but that is another template specialization. Not the same template we are partially specializing when writing our definition.

Upvotes: 4

Related Questions