Reputation: 1577
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?
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
Reputation: 170074
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