Wrong static const initialization that compiles and works - c++

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

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.

Related

C++ and C++11 class static member, double should use "constexpr" while int can be "const", why?

A structure C defined several static const members like this:
Code is like below:
#include<stdio.h>
struct C{
static int i;
static const int j=1;
static constexpr double d=1;
static const double d1=1.0;
};
int main(){
return 0;
}
Compilation will lead to error:
$g++ testStatic.cpp -std=c++11
testStatic.cpp:6:25: error: in-class initializer for static data member of
type 'const double' requires 'constexpr' specifier
[-Wstatic-float-init]
static const double d1=1.0;
^ ~~~
testStatic.cpp:6:5: note: add 'constexpr'
static const double d1=1.0;
^
constexpr
1 error generated.
Why so weird
Why static int can be const,double should be constexpr,what's the rational
const follows the original language specification defined in C++98 and C++03. It was generally disallowed to supply an in-class initalizers for static const members in C++98. The possibility to do so for static const objects of integral and enum types in C++98 was part of special treatment given to these types.
constexpris a new feature introduced in C++11. It is designed differently and works uniformly for all types.
So, you can just use constexpr for both integer and floating point types and forget about any non-uniformities.
If you continue to use const in such contexts, you will have to deal with C++98 legacy. However, C++17 will introduce inline variables, which should also make it possible to use in-class initializers for inline static const objects of any type.

Mixing constexpr declarations and const definitions

I came across the following situation:
struct Foo
{
static constexpr char s[] = "Hello world";
};
const char Foo::s[];
This code snippet compiles with Clang 3.7 (with -std=c++11 and -std=c++14), but GCC (4.8, 6.0, same language settings) gives the error I would have expected:
GCC 4.8:
in.cpp:6:19: error: redeclaration ‘Foo::s’ differs in ‘constexpr’
const char Foo::s[];
^
in.cpp:3:27: error: from previous declaration ‘Foo::s’
static constexpr char s[] = "Hello world";
^
in.cpp:6:19: error: declaration of ‘constexpr const char Foo::s [12]’ outside of class is not definition [-fpermissive]
const char Foo::s[];
GCC 6.0:
‘constexpr’ needed for in-class initialization of static data member ‘const char Foo::s [12]’ of non-integral type [-fpermissive]
I found this old question that seems to discuss mixing constexpr and const, but it focusses on whether initializers are constant expressions, rather on whether definition and declaration can differ with regard to constness.
Is it allowed to provide the definition for a constexpr T static data member as a const T?
Your code is well-formed. The constexpr-specifier is not itself part of the type but adds const ([dcl.constexpr]/9), which is present in your second declaration. Although different declarations of one function (or function template) have to agree in constexpr-ness as per [dcl.constexpr]/1, no such rule exists for variable declarations.
See bug #58541, which basically uses your example.

Is there any way to emulate compile-time double constant with MSVS2015?

I am aware MSVS2013 (even the CTP) cannot handle static constexpr double data members, as confirmed in this question.
Now, I hoped the MSVS2015 Preview would allow me to at least use this simple construct, but alas, I get the same error. So the logical next question is: is there any way to define a compile-time double constant with MSVC2015?
Example:
template<typename T>
struct my_constant
{
static constexpr const T value = 42;
}
This gives the error:
error C2864: 'my_constant<double>::value': a static data member with an in-class initializer must have non-volatile const integral type
Which is C++03 mumbo-jumbo.
If the compiler accepts a constexpr member function
static constexpr T value() {return 42;}
then that should give you a compile-time constant.
DISCLAIMER: I never use this compiler, so can't test this.

static const double cannot have an in-class initializer. why is it so?

The problem with the following code is static member of type "const double" cannot have an in-class initializer. Why is applicable only for a 'const double'in the following code? Please help me.
class sample{
static const char mc = '?';
static const double md = 2.2;
static const bool mb = true;
};
const char sample::mc;
const double sample::md;
const bool sample::mb;
int main(){
}
The logic implemented by the C++03 language standard is based on the following rationale.
In C++ an initializer is a part of object definition. What you write inside the class for static members is actually only a declaration. So, formally speaking, specifying initializers for any static members directly inside the class is "incorrect". It is contrary to the general declaration/definition concepts of the language. Whatever static data you declare inside the class has to be defined later anyway. That's where you will have your chance to specify the initializers.
An exception from this rule was made for static integer constants, because such constants in C++ can form Integral Constant Expressions (ICEs). ICEs play an important role in the language, and in order for them to work as intended the values of integral constants have to be visible in all translation units. In order to make the value of some constant visible in all translation units, it has to be visible at the point of declaration. To achieve that the language allows specifying the initializer directly in class.
Additionally, on many hardware platforms constant integer operands can be embedded directly into the machine commands. Or the constant can be completely eliminated or replaced (like, for example, multiplication by 8 can be implemented as a shift by 3). In order to facilitate generation of machine code with embedded operands and/or various arithmetical optimizations it is important to have the values of integral constants visible in all translation units.
Non-integral types do not have any functionality similar to ICE. Also, hardware platforms do not normally allow embedding non-integral operands directly into the machine commands. For this reason the above "exception from the rules" does not extend to non-integral types. It would simply achieve nothing.
The compiler offered me to use constexpr instead of const:
static_consts.cpp:3:29: error: ‘constexpr’ needed for in-class initialization of static data member ‘const double sample::md’ of non-integral type [-fpermissive]
static_consts.cpp:7:22: error: ‘constexpr’ needed for in-class initialization of static data member ‘const double sample::md’ of non-integral type [-fpermissive]
I've just accepted the offer:
class sample{
static const char mc = '?';
static constexpr double md = 2.2;
static const bool mb = true;
};
const char sample::mc;
const bool sample::mb;
int main(){
}
And now it compiles just fine (C++11).
Pre-C++11, only const integral types could be directly initialized in the class definition. It's just a restriction imposed by the standard.
With C++11, this no longer applies.

Initializing const member within class declaration in C++

In PHP and C# the constants can be initialized as they are declared:
class Calendar3
{
const int value1 = 12;
const double value2 = 0.001;
}
I have the following C++ declaration of a functor which is used with another class to compare two math vectors:
struct equal_vec
{
bool operator() (const Vector3D& a, const Vector3D& b) const
{
Vector3D dist = b - a;
return ( dist.length2() <= tolerance );
}
static const float tolerance = 0.001;
};
This code compiled without problems with g++. Now in C++0x mode (-std=c++0x) the g++ compiler outputs an error message:
error: ‘constexpr’ needed for in-class initialization of static data member ‘tolerance’ of non-integral type
I know I can define and initialize this static const member outside of the class definition. Also, a non-static constant data member can be initialized in the initializer list of a constructor.
But is there any way to initialize a constant within class declaration just like it is possible in PHP or C#?
Update
I used static keyword just because it was possible to initialize such constants within the class declaration in g++. I just need a way to initialize a constant in a class declaration no matter if it declared as static or not.
In C++11, non-static data members, static constexpr data members, and static const data members of integral or enumeration type may be initialized in the class declaration. e.g.
struct X {
int i=5;
const float f=3.12f;
static const int j=42;
static constexpr float g=9.5f;
};
In this case, the i member of all instances of class X is initialized to 5 by the compiler-generated constructor, and the f member is initialized to 3.12. The static const data member j is initialized to 42, and the static constexpr data member g is initialized to 9.5.
Since float and double are not of integral or enumeration type, such members must either be constexpr, or non-static in order for the initializer in the class definition to be permitted.
Prior to C++11, only static const data members of integral or enumeration type could have initializers in the class definition.
Initializing static member variables other than const int types is not standard C++ prior C++11. The gcc compiler will not warn you about this (and produce useful code nonetheless) unless you specify the -pedantic option. You then should get an error similiar to:
const.cpp:3:36: error: floating-point literal cannot appear in a constant-expression
const.cpp:3:36: warning: ISO C++ forbids initialization of member constant ‘tolerance’ of non-integral type ‘const float’ [-pedantic]
The reason for this is that the C++ standard does not specifiy how floating point should be implemented and is left to the processor. To get around this and other limitations constexpr was introduced.
Yes. Just add the constexpr keyword as the error says.
I ran into real problems with this, because I need the same code to compile with differing versions of g++ (the GNU C++ compiler). So I had to use a macro to see which version of the compiler was being used, and then act accordingly, like so
#if __GNUC__ > 5
#define GNU_CONST_STATIC_FLOAT_DECLARATION constexpr
#else
#define GNU_CONST_STATIC_FLOAT_DECLARATION const
#endif
GNU_CONST_STATIC_FLOAT_DECLARATION static double yugeNum=5.0;
This will use 'const' for everything before g++ version 6.0.0 and then use 'constexpr' for g++ version 6.0.0 and above. That's a guess at the version where the change takes place, because frankly I didn't notice this until g++ version 6.2.1. To do it right you may have to look at the minor version and patch number of g++, so see
https://gcc.gnu.org/onlinedocs/cpp/Common-Predefined-Macros.html
for the details on the available macros.
With gnu, you could also stick with using 'const' everywhere and then compile with the -fpermissive flag, but that gives warnings and I like my stuff to compile cleanly.
Not great, because it's specific to gnu compilers, butI suspect you could do similar with other compilers.
If you only need it in the one method you can declare it locally static:
struct equal_vec
{
bool operator() (const Vector3D& a, const Vector3D& b) const
{
static const float tolerance = 0.001f;
Vector3D dist = b - a;
return ( dist.length2() <= tolerance );
}
};