I have a class with static const
members that I'm initializing inside the class declaration:
#include <iostream>
class Foo
{
public:
static const int i = 9;
static const float f = 2.9999;
};
int main()
{
std::cout << Foo::i << std::endl;
std::cout << Foo::f << std::endl;
return 0;
}
When compiled with GCC 4.8.2 with option --std=c++11
, it gives this compile error:
foo.cpp:7:32: error: ‘constexpr’ needed for in-class initialization of static data member ‘const float Foo::f’ of non-integral type [-fpermissive]
static const float f = 2.9999;
^
As the message indicates, the error goes away if the line is changed to static constexpr float f = 2.9999;
.
Why should the in-class static const initialization of a floating-point variable be any different from a integral variable? Aren't they both just a value of certain size (number of bytes) that is copied over (like a macro) or referred to using a pointer?
Some older answers to similar (not the same) questions on SO indicate that this is because floating point expressions might give different results between the compiled machine and the execution machine (assuming a cross-compilation scenario).
However:
the above code assigns a value directly, there is no arithmetic operation that needs to be performed to compute a value
there might be different results for integral expressions too since its underflow and overflow results are not unambiguously defined across different architectures.
Finally, what magic does constexpr
do here that const
does not? Why doesn't the language just do what constexpr
does when const
is used? I mean, why another keyword when the following statements work fine as C++ code outside a class anyway:
const int i = 9;
const float f = 2.9999;
For std::string , it must be defined outside the class definition and initialized there. static members must be defined in one translation unit to fulfil the one definition rule. If C++ allows the definition below; struct C { static const string b = "hi"; };
To initialize the const value using constructor, we have to use the initialize list. This initializer list is used to initialize the data member of a class. The list of members, that will be initialized, will be present after the constructor after colon. members will be separated using comma.
Floating-point constants specify values that must have a fractional part. Floating-point constants have a "mantissa," which specifies the value of the number, an "exponent," which specifies the magnitude of the number, and an optional suffix that specifies the constant's type(double or float).
It's just a limitation of the language, and one that has been addressed by the introduction of generalized constant expressions.
Since the original C++, only static class member constants of integral type can be initialized inline; this is the is same type restriction as for non-type template parameters. So you can combine the two like this:
struct MyTrait { static const int value = 10; };
template <int N> struct Foo;
Foo<MyTrait::value> foo;
In this usage, the static constant is not odr-used and no definition is required. I'm speculating, but I can imagine that this kind of use was the primary intention of allowing inline initialization. For all other types, you would presumably want to have a definition anyway, so you might as well put the initializer in the definition.
This isn't an excuse, of course, and I suppose the introduction of constexpr
seeks to rectify this original narrow-mindedness.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With