访问静态 constexpr float 成员时未定义的引用

undefined reference when accessing static constexpr float member

此代码有效:

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: In function main': main.cpp:(.text+0xe): undefined reference toBlob::a'

为什么我不能那样使用 constexpr float

编译器: Ubuntu clang 版本 3.5.0-4ubuntu2 (tags/RELEASE_350/final)

在 gcc 版本 4.9.1 (Ubuntu 4.9.1-16ubuntu6) 上测试,没有错误。

编辑:

如果我使用 -O1、-O2、-O3 或 -Os 可以编译,但使用 -O0

会失败

C++11 读取

A variable whose name appears as a potentially-evaluated expression is odr-used unless it is an object that satisfies the requirements for appearing in a constant expression (5.19) and the lvalue-to-rvalue conversion (4.1) is immediately applied.

很明显,l-t-r 转换会立即应用,浮点类型的 constexpr 变量可以出现在常量表达式中,如 [expr.const]/(2.7.1):

A conditional-expression is a core constant expression unless it involves one of the following as a potentially evaluated subexpression [..]

  • an lvalue-to-rvalue conversion (4.1) unless it is applied to
    • a glvalue of literal type that refers to a non-volatile object defined with constexpr, or that refers to a sub-object of such an object, or

似乎是一个 Clang 错误。

有趣的是,如果我们改用 Blob::aclang 不会抱怨:

auto c = Blob::a;

这对于确定它是否被 odr 使用无关紧要。所以这看起来像一个 clang 错误,我可以在 clang 3.7 using no optimization only. We can tell this is an odr issue since adding a out of class definition fixes the issue (see it live 上重现):

constexpr float Blob::a ;

那么什么时候需要定义一个static constexpr class成员呢?这在 9.4.2 [class.static.data] 部分中有所介绍,它说(强调我的前进):

A static data member of literal type can be declared in the class definition with the constexpr specifier; if so, its declaration shall specify a brace-or-equal-initializer in which every initializer-clause that is an assignment-expression is a constant expression. [ Note: In both these cases, the member may appear in constant expressions. —end note ] The member shall still be defined in a namespace scope if it is odr-used (3.2) in the program and the namespace scope definition shall not contain an initializer.

如果是odr-used,需要定义。它是 odr 使用的吗?不它不是。 3.2 [basic.def.odr] 部分中的原始 C++11 措辞说:

An expression is potentially evaluated unless it is an unevaluated operand (Clause 5) or a subexpression thereof. A variable whose name appears as a potentially-evaluated expression is odr-used unless it is an object that satisfies the requirements for appearing in a constant expression (5.19) and the lvalue-to-rvalue conversion (4.1) is immediately applied.

a 满足这两个条件,它是一个常量表达式并且立即应用左值到右值的转换。 Defect Report 712 更改了适用于 C++11 的措辞,因为它是一个缺陷报告,3.2 现在说:

A variable x whose name appears as a potentially-evaluated expression ex is odr-used unless applying the lvalue-to-rvalue conversion (4.1) to x yields a constant expression (5.19) that does not invoke any non-trivial functions and, if x is an object, ex is an element of the set of potential results of an expression e, where either the lvalue-to-rvalue conversion (4.1) is applied to e, or e is a discarded-value expression

匹配的潜在结果是:

If e is an id-expression (5.1.1), the set contains only e.

它是一个常量表达式,并且应用了左值到右值的转换,因此它不被 odr 使用。