静态 constexpr 模板成员在专用时提供未定义的引用
static constexpr template member gives undefined-reference when specialized
以下代码给出了未定义的引用链接错误:
template<int>
struct X {
static constexpr int x = 0;
};
template<>
constexpr int X<1>::x;
int main()
{
return X<1>::x;
}
但我不知道为什么。
是否可以定义数据成员而不专门化整个模板?
需要明确的是:这段代码编译良好,但给出了链接器错误(未定义的引用)。
是否可以在不[专用化]整个模板的情况下定义数据成员?
static
类模板的数据成员都可以显式专用化 ([temp.expl.spec]),但是,如果您希望这样做,则不能已经为类模板中的成员指定初始值设定项 (class.static.data)。那是
如果我们用const
替换constexpr
,这段代码就可以了:
template<int>
struct X {
static const int x;
};
template<int I>
const int X<I>::x = 0;
template<>
const int X<1>::x = 1;
但是这段代码不会很好:
template<int>
struct X {
static const int x = 0;
};
template<>
const int X<1>::x = 1;
您可以看到不同之处在于我们初始化主模板变量的位置。
现在,如果我们希望将const
替换为constexpr
,那么我们需要提供一个初始值设定项 (class.static.data):
文本类型的
static
数据成员可以在 带有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 来避免需要专门化我们的特质类:
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);
}
由于明确专业化,您偶然发现了一个"小"问题。如果我们参考 [temp.expl.spec]/13:
模板的静态数据成员的显式专用化或 静态数据成员模板的显式专用化是一个 定义,如果声明包含初始值设定项;否则,它 是一个声明。[ 注意:静态数据成员的定义 需要默认初始化的模板必须使用 大括号初始化列表:
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
变量。这很奇怪。
像这样。
template<int i>
struct X {
static constexpr int x = i==0?2:10;
};
int main()
{
return X<1>::x;
}
请注意,您不需要在类外定义它。
- 对C宏的未定义引用,但在定义它时会出现重新定义错误
- 编译时的 CImg 库返回对"__imp_SetDIBitsToDevice"的未定义引用
- 对Py_Initialize()的未定义引用
- 使用mysql c++连接器的未定义引用
- 对 Scalar ::Scalar() 的未定义引用
- 对复制 CTOR 和 CTOR 的未定义引用
- 对显式实例化的模板函数的未定义引用
- TensorRT (C++ API) 对"createNvOnnxParser_INTERNAL"的未定义引用
- 2个模板化类的非模板友元函数未定义引用错误
- 编译 libfluid 样本控制器时对"event_base_del_virtual"的未定义引用
- 获取对function_name的未定义引用
- 对 'std::thread::_M_start_thread CMake 的未定义引用进行基准测试
- 对结构方法的未定义引用
- 使用内联函数 c++ 的未定义引用
- 对 CMake 中'cudaRegisterLinkedBinary'链接错误的未定义引用?
- 对 DLOPEN 的未定义引用
- QT C++中对全局变量的未定义引用
- 快速数学导致对"__pow_finite"的未定义引用
- 对 boost::system::d etail::system_category_instance 的未定义引用,从
- OpenCV 3.4.3 中对 'cv::String::d eallocate()' 错误的未定义引用