C++11 中的延迟初始化顺序
Deferred initialisation order in C++11
考虑以下代码,分为三个编译单元:
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;
代码使用 gcc (-std=c++11) 构建并运行良好,产生输出:
c
b
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.
请注意 a.cpp
不知道 B
和 C
的存在,并且 B
和 C
与 [= 的唯一交互24=] 是 GetA()
和 A::Register()
在构造各自实例期间的调用。
据我所知,B
和 C
实例未使用 ODR,当然也不是来自 main()
的翻译单元。他们的初始化显然有副作用,但在我看来,不能保证这种初始化会在进入 main()
之前发生,或者在 main()
打印注册的字符串之前发生——或者根本不会发生。
所以 - 最后 - 我的问题是这个: B
和 C
实例是否在 main()
打印之前初始化注册的字符串 不是 符合标准,而是 gcc 的实现定义的行为?
如果按标准保证,怎么保证?
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 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
由于 b.cpp
和 c.cpp
都没有使用函数或变量,它们的静态变量可能未初始化(关于动态初始化),因此它们的初始化会产生副作用可能不可见。
在实践中,我希望翻译单元静态链接时显示的初始化行为,以及动态加载(共享库)时可能出现的非初始化行为。但这两者都没有得到标准的保证,因为它没有指定共享库的行为方式。
考虑以下代码,分为三个编译单元:
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;
代码使用 gcc (-std=c++11) 构建并运行良好,产生输出:
c
b
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.
请注意 a.cpp
不知道 B
和 C
的存在,并且 B
和 C
与 [= 的唯一交互24=] 是 GetA()
和 A::Register()
在构造各自实例期间的调用。
据我所知,B
和 C
实例未使用 ODR,当然也不是来自 main()
的翻译单元。他们的初始化显然有副作用,但在我看来,不能保证这种初始化会在进入 main()
之前发生,或者在 main()
打印注册的字符串之前发生——或者根本不会发生。
所以 - 最后 - 我的问题是这个: B
和 C
实例是否在 main()
打印之前初始化注册的字符串 不是 符合标准,而是 gcc 的实现定义的行为?
如果按标准保证,怎么保证?
Is the fact that the
B
andC
instances are initialised beforemain()
prints the registered strings due not to the standard, but instead to gcc's implementation-defined behaviour?
它不是标准保证的。引用中最相关的部分:
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
由于 b.cpp
和 c.cpp
都没有使用函数或变量,它们的静态变量可能未初始化(关于动态初始化),因此它们的初始化会产生副作用可能不可见。
在实践中,我希望翻译单元静态链接时显示的初始化行为,以及动态加载(共享库)时可能出现的非初始化行为。但这两者都没有得到标准的保证,因为它没有指定共享库的行为方式。