double 类型的静态 class 成员的常量表达式初始值设定项

Constant expression initializer for static class member of type double

在 C++11 和 C++14 中,为什么在以下代码段中需要 constexpr

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

而这个会产生编译器错误:

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

并且(更令人惊讶的是)这个编译没有错误?

class Foo {
    static const double X;
};

const double Foo::X = 0.75;

在C++03中我们只允许为枚举类型的const整型静态成员变量提供一个in class初始化器,在C++11中我们可以在in中初始化一个字面量类型的静态成员class 使用 constexpr。此限制在 C++11 中保留用于 const 变量,主要是为了与 C++03 兼容。我们可以从 closed issue 1826: const floating-point in constant expressions 中看到:

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 最终关闭了这个请求,认为它不是缺陷 (NAD),基本上是说:

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

供参考 N1804 最接近 C++03 的标准草案在 9.4.2 [class.static.data] 部分公开提供说:

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.

和 C++11 标准草案 9.4.2 [class.static.data] 说:

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. [...]

这在 C++14 标准草案中几乎相同。

In-class static const "definitions" 其实就是声明。定义变量时,编译器会为该变量分配内存,但这里不是这种情况,即获取这些 static-const-in-class 的地址格式不正确,NDR。

这些东西应该写到代码中,但是对于浮点类型来说不太容易做到,因此是不允许的。

通过在 class 之外定义静态 const 变量,您向编译器发出信号,这是真正的定义 - 具有内存位置的真实实例。