And*_*zos 5 c++ constexpr c++11
我有一个像:
enum E
{
TYPE_FLOAT,
TYPE_CHAR,
TYPE_INT
}
Run Code Online (Sandbox Code Playgroud)
我想创建一个编译时映射,以获得适合类型的E:
GetE<float> // returns TYPE_FLOAT
GetE<char> // returns TYPE_CHAR
GetE<int> // returns TYPE_INT
Run Code Online (Sandbox Code Playgroud)
我想到了:
template<class T> struct GetE;
template<> struct GetE<float> { static constexpr E type = TYPE_FLOAT; };
template<> struct GetE<char> { static constexpr E type = TYPE_CHAR; };
template<> struct GetE<int> { static constexpr E type = TYPE_INT; };
Run Code Online (Sandbox Code Playgroud)
但我得到的错误如下:
undefined reference to `GetE<int>::type'
Run Code Online (Sandbox Code Playgroud)
什么是最好的方法呢?为什么错误呢?
这取决于你如何使用这些常量表达式.
ODR(单一定义规则)指出
(§3.2/ 2)[...]一个名称显示为潜在评估表达式的变量是odr-used,除非它是一个满足出现在常量表达式(5.19)和左值的要求的对象.立即应用右值转换(4.1).[...]
(然后,许多特殊规则,例外和异常例外如下.)
任何变量是 ODR使用的,必须有一个确切的定义.你的常量表达式有一个声明,但不是一个定义,所以这很顺利,除非你使用其中一个.
例如,以下情况很顺利:
int main() {
E e = GetE<float>::type;
return 0;
}
Run Code Online (Sandbox Code Playgroud)
但这不是:
void f(const E &)
{ }
int main() {
f(GetE<float>::type);
return 0;
}
Run Code Online (Sandbox Code Playgroud)
因为f需要一个(const)引用,所以不能立即应用左值到右值的转换,因此这构成了一个odr用法.编译器会抱怨它错过了一个定义.
(备注.正如ShafikYaghmour发现的那样(请参阅注释),如果编译器使用优化,您可能不会抱怨,因为参考可能会被优化掉.要重现编译器投诉,请使用-O0标志(或类似的,取决于编译器) .)
要解决这个问题,可以通常的方式提供所需的定义,即在struct-definition之外:
constexpr E GetE<float>::type;
constexpr E GetE<char>::type;
constexpr E GetE<int>::type;
Run Code Online (Sandbox Code Playgroud)
但由于这必须在.cpp(而不是头文件)中发生,因此您最终必须在两个不同的地方维护声明和定义,这很麻烦.
您在评论中刚刚建议的解决方案,即定义constexpr(和内联)函数,听起来是正确的:
template <class T> constexpr E GetE();
template <> constexpr E GetE<float>()
{ return TYPE_FLOAT; }
template <> constexpr E GetE<char>()
{ return TYPE_CHAR; }
template <> constexpr E GetE<int>()
{ return TYPE_INT; }
void f(const E &)
{ }
int main() {
E e = GetE<float>();
f(GetE<float>());
return 0;
}
Run Code Online (Sandbox Code Playgroud)
| 归档时间: |
|
| 查看次数: |
1981 次 |
| 最近记录: |