在模板 class 内定义的枚举上嵌套 class 的部分特化

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

请看下面代码:

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 生成错误信息:

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

但是,GCC 和 Clang 似乎接受此代码。

哪个是正确的?

编辑

在旧版本的 VC++ 上生成以下错误消息:

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

所以我认为 VC++ 拒绝编译的原因是 enum class E 是在模板 class 中定义的。事实上,如果我将 enum class E 移到 X 之外,错误就会消失(在旧版本和最近的 15.7.5 中)。

这种情况真的是依赖非类型模板参数的部分特化吗?

GCC 和 Clang 接受它是正确的。这都是根据[temp.class.spec]/5,其中还有一个支持示例:

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 ]

使用 MSVC 的解决方法可能是尝试在命名空间范围内专门化成员模板。


至于你的编辑,我想说 MSVC 仍然是错误的。相关标准在 [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 ]

将其应用于 struct Y<U, E::e0> 中的示例:枚举的类型是否取决于 Y 的另一个模板参数?答案是不。它当然取决于 X<T>,但那是 另一个 模板专业化。与我们在编写定义时部分专门化的模板不同。