Deferred initialisation order in C++11

匆匆过客 提交于 2019-12-23 16:13:38

问题


Consider the following code, split across 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 runs fine using gcc (-std=c++11), producing the output:

c
b

Now, referring to cppreference.com:

Deferred dynamic initialization

It is implementation-defined whether dynamic initialization happens-before the first statement of the main function (for statics) or the initial function of the thread (for thread-locals), or deferred to happen after.

If the initialization of a non-inline variable is deferred to happen after the first statement of main/thread function, it happens before the first odr-use of any variable with static/thread storage duration defined in the same translation unit as the variable to be initialized. If no variable or function is odr-used from a given translation unit, the non-local variables defined in that translation unit may never be initialized (this models the behavior of an on-demand dynamic library). However, as long as anything from a 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 a.cpp is unaware of the existence of B and C, and that the only interactions of B & C with A are the invocations of GetA() and A::Register() during construction of their respective instances.

As far as I can see, the B & C instances are not ODR-used, and certainly not from main()'s translation unit. Their initialisation clearly has side effects, but it seems to me that there's no guarantee that this initialisation will occur before entry to main(), or before main() prints the registered strings - or indeed at all.

So - finally - my question is this: Is the fact that the B and C instances are initialised before main() prints the registered strings due not to the standard, but instead to gcc's implementation-defined behaviour?

If it is guaranteed by the standard, how?


回答1:


Is the fact that the B and C instances are initialised before main() prints the registered strings due not to the standard, but instead to gcc's implementation-defined behaviour?

It is not guaranteed by standard. The most relevant part of the quote:

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

Since no function, nor variable has been odr used from b.cpp nor c.cpp, their static variables may be uninitialized (in regard to dynamic initialization) and therefore the side-effects of their initialization might not be visible.


In practice, I would expect the shown, initializing behaviour when the translation units are statically linked, and the possible non-initializing behaviour when they are dynamically loaded (shared library). But neither is guaranteed by the standard, as it doesn't specify how shared libraries behave.



来源:https://stackoverflow.com/questions/45210561/deferred-initialisation-order-in-c11

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!