17

In C++11 and C++14, why do I need constexpr in the following snippet:

class Foo {
    static constexpr double X = 0.75;
};

whereas this one produces a compiler error:

class Foo {
    static const double X = 0.75;
};

and (more surprisingly) this compiles without errors?

class Foo {
    static const double X;
};

const double Foo::X = 0.75;
Shafik Yaghmour
  • 154,301
  • 39
  • 440
  • 740
Stefano Sanfilippo
  • 32,265
  • 7
  • 79
  • 80
  • 2
    Mainly compatibility with C++03, [here is a summary](http://stackoverflow.com/a/28846608/1708801) – Shafik Yaghmour Jun 09 '15 at 20:35
  • One syntax for compile time constants, distinct from the syntax for creating a `static` member variable that is `const` with a default (and thus singular) value. Perhaps part of the logic here is that const can be violated, and if you are saying that you want to be able to take the address of the thing, you are possibly up to such shenanigans. – kfsone Jun 09 '15 at 20:54
  • @Stefano Sanfilippo: Why is the last example described as "more surprising"? It is actually the baseline behavior present in the language since the beginning of times. – AnT stands with Russia Jun 09 '15 at 20:55
  • 1
    @AnT well that is what issue 1826 I quote below is about, in C++11 people find this surprising and inconsistent. – Shafik Yaghmour Jun 09 '15 at 21:05

2 Answers2

14

In C++03 we were only allowed to provide an in class initializer for static member variables of const integral of enumeration types, in C++11 we could initialize a static member of literal type in class using constexpr. This restriction was kept in C++11 for const variables mainly for compatibility with C++03. We can see this from closed issue 1826: const floating-point in constant expressions which says:

A const integer initialized with a constant can be used in constant expressions, but a const floating point variable initialized with a constant cannot. This was intentional, to be compatible with C++03 while encouraging the consistent use of constexpr. Some people have found this distinction to be surprising, however.

CWG ended up closing this request as not a defect(NAD), basically saying:

that programmers desiring floating point values to participate in constant expressions should use constexpr instead of const.

For reference N1804 the closest draft standard to C++03 publicly available in section 9.4.2 [class.static.data] says:

If a static data member is of const integral or const enumeration type, its declaration in the class definition can specify a constant-initializer which shall be an integral constant expression (5.19). In that case, the member can appear in integral constant expressions. The member shall still be defined in a namespace scope if it is used in the program and the namespace scope definition shall not contain an initializer.

and the draft C++11 standard section 9.4.2 [class.static.data] says:

If a non-volatile const static data member is of integral or enumeration type, its declaration in the class definition can specify a brace-or-equal-initializer in which every initializer-clause that is an assignment expression is a constant expression (5.19). 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. [...]

this is pretty much the same in the draft C++14 standard.

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

In-class static const "definitions" are actually declarations. When a variable is defined, the compiler allocates memory for that variable, but that is not the case here, i.e. taking address of these static-const-in-class things is ill formed, NDR.

These things are supposed to be worked into the code, but that is not so easy to do with floating point types, therefore it is not allowed.

By defining your static const variables outside class you are signalling to the compiler that this is real definition - real instance with memory location.

Karlis Olte
  • 353
  • 1
  • 3
  • 10
  • It's actually ill-formed NDR, not UB. – Brian Bi Jun 09 '15 at 20:54
  • *"but that is not so easy to do with floating point types, therefore it is not allowed"* That might have been the case in C++98/03, but C++11 introduced `constexpr`, which allows `struct X { constexpr static double d = 5.0; };` – dyp Jun 10 '15 at 01:11
  • @dyp Are you saying that also floating-point types are worked into the code?! I thought that it is a "real definition", i.e. constexpr double being stored somewhere in memory. – Karlis Olte Jun 10 '15 at 06:42
  • No, a static data member of a class with an initializer in-class is not a definition, no matter if `const`, `constexpr` or both. You can use its *value* under certain circumstances (non-odr-usage), but if you need e.g. its address, a definition is still required. -- In other words, `constexpr` does not affect whether a declaration is also a definition. – dyp Jun 10 '15 at 08:58