Search code examples
c++c++14c++98static-initializationstatic-order-fiasco

Do we have a static initialisation order fiasco while initialising variables inside the class


I am trying to refactor our old code from c++98 to c++14. And the need to compile with both older gcc(c++98) and newer gcc(c++14). This is how our older code looks like (This is completely made up example, of our complicated code. The static consts fromthis class is used in another class).

// BankAccount.h
namespace bank {
class BankAccount {
 public:
    static const unsigned int account_number = 123456789;
    static const double balance = 0.0;
    BankAccount();
    ....
};
}

// BankAccount.cpp
namespace bank {
    BankAccount::BankAccount() {}
    ....
}

Looks like from c++11 only ints and enums are only allowed to initialise with in class declaration.

Question: By initialising the static const variables inside the class declaration like above does it avoid the static initialisation order fiasco ? Looks like with the above code, we never observed this static initialisation order problem, Should i be worried about the problem if i moved the static double and floats to .cpp file ?


Solution

  • (Update: As the question has been changed to static const members)

    By declaring them static const, it is safe for integer variables. In modern C++, there is no difference to declaring them as static constexpr. It is guaranteed by the language that you will not run into problems of accessing non initialized (or rather zero-initialized) numerical values.

    For floating points like double, it should not compile unless you use constexpr. However, that is a feature which is not available on C++98. Without constexpr, you will get a compile error like that:

    // gcc
    example.cpp:6:25: error: ‘constexpr’ needed for in-class initialization of static data member ‘const double bank::BankAccount::balance’ of non-integral type [-fpermissive]
        6 |     static const double balance = 0.0;
          |                         ^~~~~~~
    

    Or only with non-standard features:

    // clang with -Wall -std=c++98
    example.cpp:6:25: warning: in-class initializer for static data member of type 'const double' is a GNU extension [-Wgnu-static-float-init]
        static const double balance = 0.0;
    

    (Old answer without declaring them const, but having them non-const)

    Are you sure about the example? I don't think it will compile (both in C++98 and modern C++). You will get something like that unless you move the initialization out of the class definition:

    // gcc
    example.cpp:5:30: error: ISO C++ forbids in-class initialization of non-const static member ‘bank::BankAccount::account_number’
        5 |     static unsigned long int account_number = 123456789;
          |                              ^~~~~~~~~~~~~~
    example.cpp:6:19: error: ‘constexpr’ needed for in-class initialization of static data member ‘double bank::BankAccount::balance’ of non-integral type [-fpermissive]
        6 |     static double balance = 0.0;
          |                   ^~~~~~~
    
    // clang
    example.cpp:5:30: error: non-const static data member must be initialized out of line
        static unsigned long int account_number = 123456789;
                                 ^                ~~~~~~~~~
    example.cpp:6:19: error: non-const static data member must be initialized out of line
        static double balance = 0.0;
                      ^         ~~~
    

    If you move it out, then you may end up with the static initialization order fiasco. The values will start as being zero initialized and then it is depending on the linker when the real initialization code will be executed.

    It would be safe if the variable could be declared as constants though.