Lazy initialization order in C ++ 11

Consider the following code, divided into three compilation units:

a.h

:

struct A
{
    void Register(const char* s);

    const char* m_s[10];
    int m_i = 0;
};

A& GetA();

      

a.cpp

:

#include "a.h"
#include <stdio.h>

void A::Register(const char* s)
{
    m_s[m_i++] = s;
}

A& GetA()
{
    static A instance;
    return instance;
}

int main(int argc, char* argv[])
{
    A& a = GetA();
    int n = a.m_i;
    for (int i = 0; i < n ; ++i)
        printf("%s\n", a.m_s[i]);
    return 0;
}

      

b.cpp

:

#include "a.h"
struct B
{
    B() { GetA().Register("b"); }

    static B* instance;
};
B* B::instance = new B;

      

c.cpp

:

#include "a.h"
struct C
{
    C() { GetA().Register("c"); }

    static C* instance;
};
C* C::instance = new C;

      

The code builds and works fine using gcc (-std = c ++ 11) creating output:

c
b

      

Now referring to cppreference.com :

Lazy dynamic initialization

It is implementation-defined whether dynamic initialization occurs before the first statement of the main function (for statics) or the initial thread function (for thread-locales) or deferred to occur after.

If the initialization of a non-built-in variable is deferred after the first statement of the main / thread function, this occurs until the first use of odr of any variable with a static / thread storage duration, specified in the same translation unit as the variable to be initialized. If no variable or function is used by odr from a given translation unit, the nonlocal variables defined in that translation unit can never be initialized (this simulates the dynamic library on demand behavior). However, as long as anything from the TU is odr-used, all non-local variables whose initialization or destruction has side effects will be initialized, even if they are not used in the Program.

Note that he a.cpp

is unaware of the existence of B

and C

and that the only interactions with B

and C

with A

are calls GetA()

and A::Register()

during construction their respective cases.

As far as I can tell, instances B

and are C

not used by the ODR and certainly not from the translation unit main()

. Their initialization clearly has side effects, but it seems to me that there is no guarantee that this initialization will happen before entering main()

, or before it main()

prints out the registered lines - or even at all.

Now - finally - my question . Is the fact that the instances are B

and are C

initialized before main()

prints the registered lines due not to the standard, but instead to gcc-defined behavior?

If this is guaranteed by the standard, how?

+3


source to share


1 answer


Is the fact that the instances are B

and are C

initialized before it main()

prints out the registered lines because of not the standard but instead gcc-defined behavior?

not guaranteed by the standard. The most important part of the quote:

If no variable or function is used by odr from a given translation unit, non-local variables defined in that translation unit can never be initialized



Since no function or variable has been used by odar from b.cpp

and c.cpp

, their static variables may be uninitialized (with respect to dynamic initialization) and therefore the side effects of their initialization may not be visible.


In practice, I would expect the shown, initializing behavior when translation units are statically linked, and possible uninitializing behavior when they are dynamically loaded (shared library). But none of them are guaranteed by the standard, as it does not specify how shared libraries behave.

0


source







All Articles