“为什么编译器希望我们像这样专门化结构 X”——这不是错误消息所说的内容。你不need这样做,你真的不应该这样做,除非你想要的是部分专业化和仅为该部分专业化定义的静态成员。
问题是template<typename T2, typename T = int> struct X
是一个类模板,具有two模板参数。第二个具有默认模板参数的事实并没有改变仍然有两个参数的事实。
因此,您需要将类模板成员定义为属于具有两个参数的类模板,如下所示:
template<typename T2, typename T>
double X<T2, T>::f = 14.0;
标准中的相关段落(N4527,当前草案):
[14.5.1p3]
当成员函数、成员类、成员枚举、静态
定义了数据成员或类模板的成员模板
在类模板定义之外,成员定义是
定义为模板定义,其中模板参数是
那些类模板。使用的模板参数的名称
成员的定义可能与模板不同
类模板定义中使用的参数名称。模板
成员中类模板名称后面的参数列表
定义应按照与使用的顺序相同的顺序命名参数
在成员的模板参数列表中。每个模板参数
pack 应在模板参数列表中用省略号扩展。
[14.1p9]
[...]默认模板参数不得在模板参数列表类成员的定义
出现在成员班级之外的模板。 [...]
正如上面引用中所指定的,模板参数的实际名称(T2
and 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成员的模板参数列表
专业化应与类的模板参数列表相匹配
模板部分专业化。 a 的模板参数列表
类模板部分特化的成员应匹配
类模板部分特化的模板参数列表。 A
类模板特化是一个独特的模板。成员为
类模板部分特化与成员无关
主模板的。 [...]