假设我有这段代码
template<typename T2, typename T = int>
struct X
{
static double f;
};
template<typename T>
double X<T>::f = 14.0;
如果我尝试编译clang,请给我以下错误
声明的嵌套名称说明符'X ::'不引用a 类,类模板或类模板部分特化
和GCC:
错误:非模板'double X :: f'
的模板定义
问题是:
为什么编译器希望我们像这样专门化struct X:
template<typename T2>
struct X<T2,int>
{
static double f;
};
第一个声明有int
作为默认参数,为什么编译器不选择这个声明?
我搜索了standard锚[temp.spec],但没有帮助。
在SO {
>上回答one之后,我问了这个问题感谢您的帮助!
答案 0 :(得分:8)
“为什么编译器希望我们像这样专门化struct X” - 这不是错误消息所说的。你不需要来做这件事,你真的不应该这样做,除非你想要的是部分特化和仅为该部分特化定义的静态成员。
问题是template<typename T2, typename T = int> struct X
是一个具有两个模板参数的类模板。第二个具有默认模板参数的事实不会改变仍然存在两个参数的事实。
因此,您需要将类模板成员定义为属于具有两个参数的类模板,如下所示:
template<typename T2, typename T>
double X<T2, T>::f = 14.0;
标准中的相关段落(N4527,当前草案):
[14.5.1p3]
当一个成员函数,一个成员类,一个成员枚举,一个静态 定义数据成员或类模板的成员模板 在类模板定义之外,成员定义是 定义为 template-parameters 所在的模板定义 那些类模板。使用的模板参数的名称 在成员的定义中可能与模板不同 类模板定义中使用的参数名称。模板 成员中的类模板名称后面的参数列表 定义应按与所用参数相同的顺序命名参数 在成员的模板参数列表中。每个模板参数 pack应在模板参数列表中使用省略号进行扩展。
[14.1p9]
[...]默认 template-argument 不得在。中指定 template-parameter-lists 类成员定义 出现在成员班级之外的模板。 [...]
如上面引用中所述,模板参数(T2
和T
)的实际名称无关紧要,它们可能与类模板定义中的不同,但它们不同需要在成员的定义内保持一致。也就是说,你可以这样做
template<typename T, typename U>
double X<T, U>::f = 14.0;
它仍然会定义正确的X
类模板的成员。但是,使用相同的名称可以使您在阅读代码时更容易理解。
通过在原始示例中定义f
之前定义部分特化,template<typename T> double X<T>::f = 14.0;
成为部分特化f
的成员template<typename T2> struct X<T2,int>
的有效定义,并且只有那个模板(部分特化是模板本身)。主模板f
的成员template<typename, typename> struct X
仍未定义。
相关措辞见[14.5.5.3p1]:
类模板partial的成员的模板参数列表 specialization应匹配类的模板参数列表 模板部分专业化。 a的模板参数列表 类模板部分特化的成员应匹配 模板参数列表的类模板部分特化。一个 类模板特化是一个独特的模板。的成员 类模板部分特化与成员无关 主要模板。 [...]