Wrong static const initialization that compiles an

2019-01-28 02:05发布

问题:

As far as I know, you can only initialize static const members in the same line of their declaration if they are integral types . However, I was still able to initialize and use some static const doubles:

// compiles and works, values are indeed doubles
struct Foo1{ 
    static const double A=2.5;
    static const double B=3.2;
    static const double C=1.7;
};

// compiles, but values are cast to int
struct Foo2{
    static const int A=2;
    static const int B=3;
    static const double C=B/A; //becomes 1
};

// does not compile, Foo3::B cannot appear in a constant-expression
struct Foo3{ 
    static const int A=2;
    static const double B=3;
    static const double C=A/B; 
};

// does not compile, a cast to a type other than an integral or enumeration
// cannot appear in a constant-expression
struct Foo4{ 
    static const int A=2;
    static const int B=3;
    static const double C=(double)A/B; 
};

Foo2 compiles but Foo2::C becomes 1, so maybe it is treated as an int as it is numerically one. Foo3 and Foo4 don't even compile, as expected. However, I don't understand why Foo1 both compiles and works correctly. Is this specific usage accepted? Is it because of some optimization? ( I've tried using -O1 and -O0)

Note: using GNU 5.2.0 with cmake and setting the standard to C++98. Switching to C++11 works fine ( that is, does not compile and asks to switch those members to constexpr).

回答1:

The Foo1 case is indeed non-conforming and if we build using -std=c++98 -pedantic gcc will warn as follows (see it live):

error: floating-point literal cannot appear in a constant-expression
 static const double A=2.5;
                       ^
warning: ISO C++ forbids initialization of member constant 'Foo1::A' of non-integral type 'const double' [-Wpedantic]

While compiling without -pedantic does not yield any error or warning (see it live)

So this must be an extension and if we use clang using -std=C++98 -pedantic we see this message:

warning: in-class initializer for static data member of type 'const double' is a GNU extension [-Wgnu-static-float-init]
static const double A=2.5;
                    ^ ~~~

which seems to confirm this is an extension.

This restriction on floating point was kept in C++11 to remain compatible with C++03 and to encourage consistent use of constexpr see: Constant expression initializer for static class member of type double.

This is also the case for Foo2 initializing C is allowed as an extension, the result of the division will be int since the type of the result depends on the type of the operands and does not depend on what you assign it to.

Update

This is a depreciated extension:

G++ allows static data members of const floating-point type to be declared with an initializer in a class definition. The standard only allows initializers for static members of const integral types and const enumeration types so this extension has been deprecated and will be removed from a future version.

There is a more detailed gcc bug report that discusses the validity of the extension and other related issues around it.

It seemed odd that using -pedantic was sufficient by itself to turn this into an error, there is a gcc bug report that covers that.