Constant expression initializer for static class m

2019-01-07 00:16发布

问题:

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;

回答1:

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 will 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.



回答2:

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.