Prevent Static Initialization Order "Fiasco", C++

Prevent static initialization order fiasco , C++

The modern, more pattern-oriented way is not to use globals in the first place.

There's no other way around it.

It wouldn't be much of a "fiasco", otherwise!

Can the static initialization order fiasco occur in C programs?

Static initialization in C does not have the same problems that C++ has.

In C, objects with static storage duration may only be initialized via constant expressions, i.e. values that can be computed at compile time, so there are no issues that can arise regarding order of initialization.

In contrast, C++ allows calling functions to initialize static objects, and the order in which those functions are called are not well-defined.

Question regarding Static Initialization Order Fiasco

All global variables (including class-level statics) are guaranteed to be initialized before main(). The order in which they are initialized between different source files is undefined.

Global Initialization Order Fiasco refers to the situation where global variables in one file are initialized with global variables from another source file, and the result depends on the initialization order. In your case, the variables are initialized with zero, so there is no "fiasco" - the program is safe.

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

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

Why can't the linker prevent the C++ static initialization order fiasco?

Linkers traditionally just link - i.e. they resolve addresses. You seem to be wanting them to do semantic analysis of the code. But they don't have access to semantic information - only a bunch of object code. Modern linkers at least can handle large symbol names and discard duplicate symbols to make templates more useable, but so long as linkers and compilers are independent, that's about it. Of course if both linker and compiler are developed by the same team, and if that team is a big corporation, more intelligence can be put in the linker, but it's hard to see how a standard for a portable language can mandate such a thing.

If you want to know more about linkers, BTW, take a look at http://www.iecc.com/linker/ - about the only book on an often ignored tool.

Static initialization order fiasco

But if it is true, and the compiler handles the implementation file after some another file, than it will set x and y to zero eliminating initialization and all possible changes in previous files?

I'm not sure what you mean by this. If x and y are defined in other files, then you have a linker clash and the program simply won't compile.

If x, y and most importantly Initializer::initCount are implemented in this way, there will be unique instances of them in the program; they are effectively global and will be initialized to 0 at program start, before any Initializer is constructed (due to inclusion of the header declaring a static instance of that class). Each construction of a static Initializer will first check whether any other Initializers have been constructed due to the if (initCount++ == 0) etc.

The first Initializer ctor to run (still before entering main) will thus set all three values.



Related Topics



Leave a reply



Submit