Static initialization in one compilation unit

Static initialization is well described here and even on this site here

And everywhere it is written that the problem will arise if there are different compilation units with related static variables. And if static variables exist in one compilation unit, there should be no problems: they will be initialized in the order of their position in the file.

But I have this code:

template <typename T> class A{ public: int _data; T _obj; A(int data) :_data(data){} }; template <typename T> class B{ public: const static B<T> nullObj; B(int data) :_a(new A<T>(data)){} A<T> *_a; }; template <typename T> class C{ public: const static C<T> nullObj; C() :_a(nullObj._a){} C(bool t) :_a(B<T>::nullObj._a){ _a->_data++; //FAILS HERE! } A<T> *_a; }; template <typename T> const B<T> B<T>::nullObj(0); template <typename T> const C<T> C<T>::nullObj(false); class _B{}; class _A{ public: _A(){}; C<_B> g; }; int main(){ return 0; } 

And it does not work at runtime before entering the main () function, because it is trying to initialize const C<T> C<T>::nullObj(false); before const B<T> B<T>::nullObj(0); will be initialized. Despite its position in one file.

Why is he trying to initialize a second static variable before the first static variable? Is there a chapter in C ++ Standart that describes the real situation with the sequence of static initialization?

+5
source share
3 answers

C ++ Standard, Section 14.7.1 [temp.inst]:

If a class template member or member template is not explicitly created or is not explicitly specialized, the specialization of this element is implicitly created when the specialization is referenced in a context that requires a member definition; in particular, initialization (and any associated side effects) of a static data member does not occur if the static data member itself is not used in such a way that a definition of the static data member is required.


C<T> is executed before B<T> , so the initialization order is well defined and equal to:

1) const static C<T> nullObj;

2) const static B<T> nullObj;

Since C dereferencing the constructor of B<T>::nullObj._a , you have undefined behavior.

Decision:

You should use the static member B<_B>::nullObj somehow to initialize it, for example. if you do this:

 class _B{}; class _A{ public: _A() : b(B<_B>::nullObj) {}; B<_B> b; C<_B> g; }; 

Then B<_B>::nullObj really initialized until the C constructor needs it

+5
source

Template instances are not defined until they are created. The first C<> instance is created in the _A constructor. And B<> is instantiated by C<> , so B<_B> is defined when instantiating C<_B> . Therefore, C<_B> to B<_B> .

You can add an explicit instance of the template template class B<_B>; before the _A definition, which I suppose should fix the order.

+2
source

The point is in the constructor _A. As the cppreference says:

The default constructor for the class T is trivial (i.e. does not perform any actions) if all of the following is true:

  • The constructor is not provided by the user (i.e. implicitly set or default)
  • T has no virtual member functions
  • T does not have virtual base classes
  • T has no non-static elements with logical or equal initializers. (since C ++ 11)
  • Each direct base T has a trivial default constructor
  • Each non-static member of a class type has a trivial default constructor.

This one is not trivially constructive

 class _A{ public: _A(){} }; 

bye this

 class _A{ public: }; 

and in the case of an example, this definitely does not have a trivial constructor:

 class _A{ public: _A(){} C<_B> g; }; 

this means that the constructor performs initialization procedures for members of the class. As stated in standard 14.7.1 [temp.inst]

initialization (and any side effects associated with it) of a static data member does not occur if the static data member itself is not used in such a way that a static data element definition is required

and since the non-trivial constructor simply provided us with the use (and not the need ) for initializing static variables, CRT starts its work and starts initializing

 const static C<T> nullObj; 

which, in turn, shares something unified, and you have undefined behavior.

To decide, as indicated in the quantum code, either initialize B<_B>::nullObj before this process, or change the design of your code.

+1
source

Source: https://habr.com/ru/post/1201221/


All Articles