以下代码给出了未定义的引用链接错误:
template<int>
struct X {
static constexpr int x = 0;
};
template<>
constexpr int X<1>::x;
int main()
{
return X<1>::x;
}
但我不确切知道原因。
是否可以定义数据成员而不专门化整个模板?
要明确:此代码编译良好,但提供链接器错误(未定义引用)。
答案 0 :(得分:4)
由于明确专业化,你偶然发现了一个“小”问题。如果我们引用[temp.expl.spec]/13:
模板或静态数据成员的显式特化 静态数据成员模板的显式特化是一个 如果声明包含初始化程序,则定义;否则,它 是宣言。 [注意:静态数据成员的定义 需要默认初始化的模板必须使用a 支撑-INIT-列表:
template<> X Q<int>::x; // declaration template<> X Q<int>::x (); // error: declares a function template<> X Q<int>::x { }; // definition
- 结束说明]
意味着您声明 X<1>::x
存在,但没有定义它。因此它未定义。
我觉得很疯狂,是你的编译器接受它。一般而言,如果没有定义constexpr
变量,则无法声明它们。这很奇怪。
答案 1 :(得分:3)
是否可以在没有[专门化]整个模板的情况下定义数据成员?
static
类模板的数据成员允许显式专门化([temp.expl.spec]),但是如果您希望这样做,则无法指定类模板(class.static.data)中成员的初始化程序。也就是说,
如果我们将constexpr
替换为const
,则此代码可以正常使用:
template<int>
struct X {
static const int x;
};
template<int I>
const int X<I>::x = 0;
template<>
const int X<1>::x = 1;
但是这段代码 NOT 没关系:
template<int>
struct X {
static const int x = 0;
};
template<>
const int X<1>::x = 1;
您可以看到区别在于我们初始化主模板的变量。
现在,如果我们希望将const
替换为constexpr
,那么我们必需来提供初始值设定项(class.static.data):
可以在。中声明文字类型的
static
数据成员 使用constexpr
说明符的类定义;如果是这样,其声明应指定大括号或等于初始值 其中作为赋值表达式的每个 initializer-clause 都是一个常量表达式
因此,我们最终会出现这种奇怪的情况:我们可以专注于static
成员,但如果constexpr
成员constexpr
,则template<int>
struct X_Traits{
static constexpr int value = 0;
};
template<>
struct X_Traits<1>{
static constexpr int value = 1;
};
template<int I>
struct X {
static constexpr int x=X_Traits<I>::value;
// ...
};
需要初始化程序。恕我直言,这是标准的缺点。
然而,似乎所有现代编译器都不同意。
gcc 8.0.0按原样编译(但不链接)你的代码(错误),但是如果为专业化添加初始化程序,它会抱怨重复初始化(右)。
clang 6.0.0不按原样(右)编译代码,但是当你添加初始化程序时,它可以毫无障碍地工作(错误,但这可能是标准应该规定的)
MSVC 19.00.23506不按原样编译代码(右),并且在添加初始化程序时没有编译代码(抱怨重新定义)(右)。
最后,将专业化推入辅助Traits类可能更容易:
template<int I>
struct X_Traits{
static constexpr int get_value(){
if constexpr(I==1){
return 1;
}else{
return 0;
}
}
};
template<int I>
struct X {
static constexpr int x=X_Traits<I>::get_value();
// ...
};
int main(){
static_assert(X<0>::x == 0);
static_assert(X<1>::x == 1);
}
在C ++ 17及更高版本中,我们可以使用constexpr if来避免需要专门化我们的特征类:
{{1}}
答案 2 :(得分:2)
template<int i>
struct X {
static constexpr int x = i==0?2:10;
};
int main()
{
return X<1>::x;
}
请注意,您不需要在课外定义它。