能否在不对整个模板进行[特化]的情况下定义数据成员?
可以显式特化类模板的
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):
文字类型的静态数据成员可以在类定义中使用 constexpr
说明符声明;如果这样做,则它的声明应指定一个花括号或等号初始化程序,在其中每个分配表达式的初始化器子句都是常量表达式。
所以我们最终陷入了这种奇怪的情况:我们可以专门化
static
成员,但如果它是
constexpr
,则不能,因为
constexpr
需要一个初始化程序。在我看来,这是标准的缺陷。
然而,似乎并不是所有的现代编译器都同意这一点。
gcc 8.0.0编译(但不链接)您的代码(错误),但是如果您为专业化添加初始化程序,则会抱怨重复初始化(正确)。
clang 6.0.0不会按原样编译代码(正确),但是当您添加初始化程序时,它可以无障碍地工作(错误,但这可能是标准应该规定的内容)。
MSVC 19.00.23506不会按原样编译代码(正确),并且当您添加初始化程序时,它也不会编译代码(抱怨重定义)(正确)。
最终,将专业化推入辅助Traits类可能更容易一些:
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;
};
在C++17及以上版本中,我们可以使用constexpr if来避免需要专门化特性类:
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);
}