具有默认模板参数的模板结构未实例化

Template struct with the default template argument is not instantiated

假设我有这个代码

template<typename T2, typename T = int>
struct X
{
    static double f;
};

template<typename T>
double X<T>::f = 14.0;

如果我尝试编译 clang,会出现以下错误

nested name specifier 'X::' for declaration does not refer into a class, class template or class template partial specialization

对于 GCC:

error: template definition of non-template 'double X::f'

问题是:

为什么编译器要我们像这样专门化结构 X:

template<typename T2>
struct X<T2,int>
{
    static double f;
};

第一个声明有 int 作为默认参数,为什么编译器不选择这个声明?

我在 standard 锚点 [temp.spec] 中进行了搜索,但没有帮助。

我在 SO 上回答了这个 之后问了这个问题。

感谢您的帮助!

"Why the compiler want us to specialize the struct X like that" - 这不是错误消息所说的。你不需要这样做,而且你真的不应该这样做,除非你想要的是部分特化和仅为该部分特化定义的静态成员。

问题是 template<typename T2, typename T = int> struct X 是一个具有 两个 模板参数的 class 模板。第二个具有默认模板参数的事实并没有改变仍然有两个参数的事实。

因此,您需要将 class 模板成员定义为属于具有两个参数的 class 模板,如下所示:

template<typename T2, typename T>
double X<T2, T>::f = 14.0;

标准(N4527,当前草案)中的相关段落:

[14.5.1p3]

When a member function, a member class, a member enumeration, a static data member or a member template of a class template is defined outside of the class template definition, the member definition is defined as a template definition in which the template-parameters are those of the class template. The names of the template parameters used in the definition of the member may be different from the template parameter names used in the class template definition. The template argument list following the class template name in the member definition shall name the parameters in the same order as the one used in the template parameter list of the member. Each template parameter pack shall be expanded with an ellipsis in the template argument list.

[14.1p9]

[...] A default template-argument shall not be specified in the template-parameter-lists of the definition of a member of a class template that appears outside of the member’s class. [...]


正如上面引用的那样,模板参数的实际名称(T2T)无关紧要,它们可以与 [=52= 中的名称不同] 模板定义,但它们需要在成员定义内保持一致。也就是说,你可以这样做

template<typename T, typename U>
double X<T, U>::f = 14.0;

它仍然会定义正确的 X class 模板的成员。但是,使用相同的名称可以使阅读代码时更容易理解。


通过在原始示例中的 f 定义之前定义偏特化,template<typename T> double X<T>::f = 14.0; 成为偏特化 template<typename T2> struct X<T2,int> 的成员 f 的有效定义,并且只有那个模板(部分特化本身就是模板)。主模板 template<typename, typename> struct X 的成员 f 仍未定义。

相关写法在[14.5.5.3p1]:

The template parameter list of a member of a class template partial specialization shall match the template parameter list of the class template partial specialization. The template argument list of a member of a class template partial specialization shall match the template argument list of the class template partial specialization. A class template specialization is a distinct template. The members of the class template partial specialization are unrelated to the members of the primary template. [...]