95

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.

ezpresso
  • 7,896
  • 13
  • 62
  • 94
  • 5
    `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.` That's the _wrong_ way to decide whether a member should be `static` or not. Never let lexical laziness decide the semantics of your code. – Lightness Races in Orbit Feb 11 '12 at 18:17
  • `That's the wrong way to decide whether a member should be static or not.` I don't agree. I think that does not matter for constant members. – ezpresso Feb 11 '12 at 19:05
  • 3
    @expresso: Not at all. You can initialise a non-`static` constant member with instance-specific information. That you've decided that your constant is a property of the type rather than of a specific instance is the reason to make it `static`, not because you fancied a typing shortcut. – Lightness Races in Orbit Feb 11 '12 at 19:31
  • @lightless: Well, it is possible, but I don't see any reason for making use of initialization of same instance-specific constants with different values. I used to use non-const class fields for that! – ezpresso Feb 11 '12 at 20:06
  • 5
    Why, if they never change after object instantiation? `struct myType { const std::time_t instantiated; myType() : instantiated(std::time(0)) {} };` Everything that _can_ be `const` *should* be `const`; that applies to `static` and non-`static` members alike. – Lightness Races in Orbit Feb 11 '12 at 20:40

5 Answers5

162

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.

Anthony Williams
  • 66,628
  • 14
  • 133
  • 155
  • Is there a link to the part of the standard where this was spec'd? I've just started using it (albeit compiling as C++14) and am delighted that it works, as I stand to save a lot of time by having the compiler do this for me. However, until reading your answer, I wasn't sure whether it _should_ be working! An official word would aid my confidence, haha. Fwiw I have it working with members like `char const n[3]{'a', 'b', 'c'};` too. – underscore_d Dec 16 '15 at 01:48
  • 2
    I wonder why `static const int` but `static constexpr float`? what does it mean `float and double are not of integral or enumeration type` ? – mrgloom Jan 11 '19 at 10:03
  • @mrgloom : yes, float and double are not. If the datatype could be translated to a whole number, then it's an integral type. – ppadhy Jan 26 '19 at 09:39
  • 2
    What is the point of this restriction? In any case, thanks for the good answer – Post Self Mar 04 '22 at 18:58
50

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.

znkr
  • 1,746
  • 11
  • 14
  • 1
    Thanks for the hint "is left to the processor". That made me thinking from "WTF is the technical difference regarding initialization between uint32_t and float?!?!?" to "Ah, ok!" :-D – IanH Feb 05 '22 at 12:31
15

Yes. Just add the constexpr keyword as the error says.

StilesCrisis
  • 15,972
  • 4
  • 39
  • 62
3

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.

nilesOien
  • 39
  • 3
2

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 );
    }
};
Peter Wood
  • 23,859
  • 5
  • 60
  • 99