此代码有效:
struct Blob {
static constexpr int a = 10;
};
int main() {
Blob b;
auto c = b.a;
}
但如果我将int
更改为float
,我会收到错误消息:
struct Blob {
static constexpr float a = 10.0f;
};
/tmp/main-272d80.o:在函数
中main': main.cpp:(.text+0xe): undefined reference to
Blob :: a'
为什么我不能以这种方式使用constexpr float
?
编译器: Ubuntu clang版本3.5.0-4ubuntu2(标签/ RELEASE_350 / final)
在gcc版本4.9.1(Ubuntu 4.9.1-16ubuntu6)上测试并且没有错误。
修改
如果我使用-O1,-O2,-O3或-Os但是会在-O0
时失败而编译答案 0 :(得分:6)
C ++ 11读取
名称显示为可能评估的表达式的变量是 odr-used 除非它是满足要求的对象 出现在常量表达式(5.19)和左值到右值 转换(4.1)立即应用。
显然,立即应用l-t-r转换,浮点类型的constexpr
变量可以按照[expr.const] /(2.7.1)出现在常量表达式中:
条件表达式是核心常量表达式,除非它 涉及以下之一作为潜在评估的子表达式 [..]
- 左值 - 右值转换(4.1),除非适用于
- 文字类型的glvalue,引用用
constexpr
定义的非易失性对象,或引用此类的子对象 对象,或
似乎是一个Clang bug。
答案 1 :(得分:3)
有趣的是,如果我们改为使用Blob::a
,clang
就不会抱怨:
auto c = Blob::a;
这对于确定它是否使用过程无关紧要。所以这看起来像clang
错误,我可以在clang 3.7上重现,不使用任何优化。我们可以说这是一个odr问题,因为添加一个课外定义可以解决问题( see it live ):
constexpr float Blob::a ;
那么你什么时候需要定义一个静态constexpr类成员?这一点在9.4.2
[class.static.data] 部分中有所说明(强调我的前进):
可以在。中声明文字类型的静态数据成员 使用constexpr说明符的类定义;如果是这样,其声明应指定一个支撑或等于初始化器 其中作为赋值表达式的每个initializer子句都是一个常量表达式。 [注意:两者都有 在这些情况下,成员可能出现在常量表达式中。 -end note] 仍然要定义成员 在命名空间范围内,如果程序中使用了odr-used(3.2),则命名空间范围定义不得 包含初始化程序。
如果使用的话,它需要一个定义。它是否经常使用?不它不是。 3.2
[basic.def.odr] 部分中的原始C ++ 11措辞说:
表达式可能会被评估,除非它是未评估的操作数(第5条)或子表达式 它们。名称显示为潜在评估表达式的变量除非是一个,否则将被使用 满足出现在常量表达式中的要求的对象(5.19)和左值到左值的对象 转换(4.1)立即应用。
a
满足两个条件,它是一个常量表达式,并且立即应用左值到右值的转换。 Defect Report 712已经改变了适用于C ++ 11的措辞,因为它是一个缺陷报告,3.2
现在说:
变量x的名称显示为ex 的潜在评估表达式,除非应用 左值到右值的转换(4.1)到x产生一个不调用任何非平凡值的常量表达式(5.19) 函数和,如果x是一个对象,ex是表达式e的潜在结果集合的元素,其中 要么将左值到右值转换(4.1)应用于e,要么e是丢弃值表达式
匹配的潜在结果是:
如果e是id-expression(5.1.1),则该集仅包含e。
它是一个常量表达式,并且应用了左值到右值的转换,因此它不会被使用。