8

This code works:

struct Blob {
    static constexpr int a = 10;
};

int main() {
    Blob b;
    auto c = b.a;
}

But if I change int to float I get an error:

struct Blob {
    static constexpr float a = 10.0f;
};

/tmp/main-272d80.o: In function main': main.cpp:(.text+0xe): undefined reference toBlob::a'

Why can't I use a constexpr float in that way?

Compiler: Ubuntu clang version 3.5.0-4ubuntu2 (tags/RELEASE_350/final)

Tested on gcc version 4.9.1 (Ubuntu 4.9.1-16ubuntu6) and there were no error.

EDIT:

It will compile if I use -O1, -O2, -O3 or -Os but fails with -O0

Shafik Yaghmour
  • 154,301
  • 39
  • 440
  • 740
Anonymous Entity
  • 3,254
  • 3
  • 27
  • 41
  • Ubuntu clang version 3.5.0-4ubuntu2 (tags/RELEASE_350/final) – Anonymous Entity Feb 01 '15 at 15:25
  • @Deduplicator `error: non-static data member cannot be constexpr; did you intend to make it static?` – Anonymous Entity Feb 01 '15 at 15:26
  • It compiles with clang 3.5.0 here: http://coliru.stacked-crooked.com/a/b4c63c92dfcc9b3d – interjay Feb 01 '15 at 15:27
  • Can't [reproduce](http://rextester.com/CASWM32415) on clang 3.4 – P0W Feb 01 '15 at 15:27
  • It compiles on my gcc too, some bug in clang? – Anonymous Entity Feb 01 '15 at 15:29
  • Definitely compiles in clang++ 3.7.0 from last weekend. – Mats Petersson Feb 01 '15 at 15:31
  • @interjay It works for me too using -O3 :) – Anonymous Entity Feb 01 '15 at 15:37
  • Reproduced when compiling on clang 3.5.0 without optimizations: http://coliru.stacked-crooked.com/a/357f3c94d573b048 – interjay Feb 01 '15 at 15:39
  • I want to say that this is invalid C++11 and valid C++14 but don't have my references on me at the moment. – Lightness Races in Orbit Feb 01 '15 at 15:41
  • *N3690: 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. ... **The member shall still be defined in a namespace scope** if it is odr-used n the program and the namespace scope definition shall not contain an initializer..* -- I don't know where it actually says the `int`version is allowed – Ryan Haining Feb 01 '15 at 15:52
  • It seems to be working by accident when compiling the OP example, because despite turning on optimization in my project, I still get the same error. – Anonymous Entity Feb 01 '15 at 15:53
  • @RyanHaining The question is whether it is odr-used or not. – interjay Feb 01 '15 at 15:57
  • Filed a [bug report](http://llvm.org/bugs/show_bug.cgi?id=22565) and it was resolved as a duplicate of [this one](http://llvm.org/bugs/show_bug.cgi?id=18781) which is similar, which confirms it is indeed a bug. – Shafik Yaghmour Feb 14 '15 at 02:54

2 Answers2

6

C++11 reads

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.

Clearly the l-t-r conversion is immediately applied, and a constexpr variable of floating point type can appear in constant expressions as per [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

Seems to be a Clang bug.

Columbo
  • 60,038
  • 8
  • 155
  • 203
3

Interestingly, if we use Blob::a instead, clang does not complain:

auto c = Blob::a;

This should not matter for determining if the it is odr-used or not. So this looks like a clang bug which I can reproduce on 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 ;

So when do you need to define a static constexpr class member? This is covered in section 9.4.2 [class.static.data] which says (emphasis mine going forward):

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.

It requires a definition if it is odr-used. Is it odr-used? No, it is not. The original C++11 wording in section 3.2 [basic.def.odr] says:

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 satisfies both conditions, it is a constant expression and the lvalue-to-rvalue conversion is immediately applied. Defect Report 712 has changed the wording which applies to C++11 since it is a defect report and 3.2 now says:

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

The potential result that matches would be:

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

it is a constant expression and the lvalue-to-rvalue conversion is applied so it is not odr-used.

Shafik Yaghmour
  • 154,301
  • 39
  • 440
  • 740