C ++: static static initialization dependent on member variable with int vs struct

Given a static member variable that is initialized from a member static variable of another class, the non-literal struct ii sometimes initialized by default to 0 or to 333 . It depends on the compilation or binding order. Pseudo code to demonstrate:

 class StaticClass: // file 'ONE.cpp/.hpp' static int i = 2 static struct ii { int a = 333 } class ABC: // file 'abc.cpp' static int abc_i = StaticClass::i // always 2 static struct abc_ii = StaticClass::ii // sometimes 0, sometimes 333 

Calling g++ -std=c++11 abc.cpp ONE.cpp && ./a.out results in i = 2 / ii = 0 (gcc 4.8.1, same thing with clang ++ 3.7; -Wall -Wextra never complains )

But calling g++ -std=c++11 ONE.cpp abc.cpp && ./a.out leads to i = 2 / ii = 333 !

The same thing happens with ONE.o abc.o vs abc.o ONE.o , as well as when concatenating files one way or another:

cat ONE.cpp abc.cpp > X.cpp && g++ X.cpp && ./a.out vs cat abc.cpp ONE.cpp > Y.cpp && g++ Y.cpp && ./a.out

Deletion turns on and moves the code in one file; when this order is launched, the initialization is 0 by default:

const OneI ABC::def_ii = StaticClass::ii; const OneI StaticClass::ii = OneI{333};

and from one to 333 with this order:

const OneI StaticClass::ii = OneI{333}; const OneI ABC::def_ii = StaticClass::ii;

Why does this happen even with two separate compilation units? Can this be somehow avoided by forcing the latter to order all the time? Is using a static pointer in ABC before StaticClass::ii safe (I would rather not do this)?

Full C ++ code:


 /* File: abc.cpp */ #include <iostream> #include "ONE.hpp" struct ABC { ABC(); static const int def_i; static const OneI def_ii; void arg_i(const int &x) { std::cout << "i = " << x << " ";}; void arg_ii(const OneI &x) { std::cout << "/ ii = " << xa << " ";}; }; ABC::ABC() { arg_i(def_i); arg_ii(def_ii); } const int ABC::def_i = StaticClass::i; const OneI ABC::def_ii = StaticClass::ii; int main() { ABC a; std::cout << '\n'; } /* End: abc.cpp */ 

 /* File: ONE.cpp */ #include <iostream> #include "ONE.hpp" const int StaticClass::i = 2; const OneI StaticClass::ii = OneI{333}; /* End: ONE.cpp */ 

 /* File: ONE.hpp */ #include <iostream> #ifndef One #define One struct OneI { OneI(int a_) : a(a_) { } int a; }; struct StaticClass { const static int i; const static OneI ii; }; #endif // One header guard /* End: ONE.hpp */ 
+7
c ++ static c ++ 11
source share
1 answer

Congratulations! You have encountered static initialization order errors .

The initialization order of static objects is not defined in several translation units.

StaticClass::ii defined in ONE.cpp , and ABC::def_ii is defined in abc.cpp . Therefore, StaticClass::ii may or may not be initialized before ABC::def_ii . Since the initialization of ABC::def_ii uses the value of StaticClass::ii , the value will depend on whether StaticClass::ii has been initialized yet † .

The order of initialization of static objects within the translation unit is defined. Objects are initialized in the order in which they are defined. Therefore, when concatenating source files, the initialization order is determined. However, when combining files in the wrong order, a certain initialization order is incorrect:

 const OneI ABC::def_ii = StaticClass::ii; // StaticClass::ii wasn't initialized yet const OneI StaticClass::ii = OneI{333}; 

Can this be somehow avoided by forcing the latter to order all the time?

The most trivial solution is to define both objects in the same translation system in the correct order. A more general solution is to initialize your static objects using Construct on First Use Idiom .

Uses a static pointer in ABC before StaticClass::ii safe (I would prefer not to use this)?

As long as the pointer dereferencing value is not used during the initialization of the static object in another translation unit where the pointed object is defined, yes, replacing ABC::def_ii pointer will be safe.


† StaticClass::ii will be initialized to zero during the static initialization phase †† . The fiasco of the order of static initialization refers to the phase of dynamic initialization †† .

†† Standard Draft C ++ [basic.start.static]

If constant initialization is not performed, a variable with a static storage duration ([basic.stc.static]) or a thread storage duration ([basic.stc.thread]) is initialized to zero ([dcl.init]). Together, zero initialization and constant initialization are called static initialization; all other initialization is dynamic initialization. Static initialization must be performed before any dynamic initialization begins. [Note: dynamic initialization of non-local variables is described in [basic.start.dynamic]; local static variables are described in [stmt.dcl]. - final note]

+4
source share

All Articles