这里cppref说,
如果非内联变量(自C ++ 17)的初始化推迟发生在主/线程函数的第一个语句之后,它发生在任何第一个odr-use 之前具有与要初始化的变量相同的转换单元中定义的静态/线程存储持续时间的变量。
后来它给出了延迟动态初始化的一个例子:
// - File 1 -
#include "a.h"
#include "b.h"
B b;
A::A(){ b.Use(); }
// - File 2 -
#include "a.h"
A a;
// - File 3 -
#include "a.h"
#include "b.h"
extern A a;
extern B b;
int main() {
a.Use();
b.Use();
}
评论说:
如果在
中使用之前进行初始化a
的第一个语句之后的某个时刻初始化main
(odr-使用File 1
中定义的函数,强制进行动态初始化要运行),那么b
将在A :: A
为什么 if 情况会发生?那么a.Use()
odr-use a
因此a
必须在此声明之前初始化吗?
答案 0 :(得分:0)
我认为你被C ++中的事物顺序误导了。
翻译单元(TU =一个.cpp文件及其标题)在C ++中没有顺序。它们可以按任何顺序编译,也可以并行编译。唯一特殊的TU是包含main()
的TU,但即使是那个也可以随时编译。
在每个翻译单元中,都有一个初始化程序出现的顺序。这也是它们被初始化的时间顺序,但它可能与它们在内存中的顺序不同(如果甚至确定 - 严格来说C ++并不强制执行)。这不会导致翻译单元的初始化程序的顺序。 在执行该翻译单元的功能之前发生,因为这些功能可能依赖于初始化的对象。
翻译单元中的功能当然可以按任何顺序出现;它们的执行方式取决于你写的内容。
现在有一些事情会产生额外的排序限制。我知道您知道某些初始化程序甚至可以在main()
启动后运行。如果发生这种情况,普通规则仍然适用于单个TU的初始值设定项必须执行该TU中的函数。
在这种情况下,TU file1
拥有b
的(默认)初始值设定项,该初始值设定项必须在同一个TU中A::A
之前运行。正如您所记录的那样,a.Use
必须在a
初始化后发生。这需要A::A
。
因此,我们有以下顺序关系(其中<
表示precedes
)
b < A::A
A::A < a
a < a.Use
因此可以传递
b < a.Use
正如您所看到的,在a.c
中使用a.Use
是安全的,因为订单A::A < a.Use
也有效。
如果A::A
取决于b
而B::B
取决于a
,可能会遇到问题。如果引入循环依赖项,则无论首先初始化哪个对象,它始终依赖于尚未初始化的对象。不要那样做。
答案 1 :(得分:0)
简而言之,为什么要打扰a
和b
的初始化顺序?
示例显示没有任何内容表明a
必须在b
之前初始化才能使程序定义明确。
extern A a;
确实在extern B b;
之前,但这与订单无关。
a.Use();
中的评估在文件3翻译的TU中b.Use();
函数的main
中的评估之前进行了排序,但这仍然无关紧要做订单。
使a.Use()
定义明确与此特定顺序无关,除非存在其他依赖关系(例如,子对象初始化意味着顺序)。
OTOH,如果你想要额外的订单,你如何指定它?
附件:
措辞&#34;发生在主/线程功能的第一个声明之后&#34;很奇怪似乎有意的是&#34;在主/线函数&#34;的第一个语句之前没有发生,并且编辑器偶尔会错过,在评估单个语句时可以有多个适用于二进制关系的评估。这源于标准,但已由P0250R3更正。
实际上,我发现这个例子来自N4727引用的标准[basic.start.dynamic] / 4:
3 非初始化odr-use 是一种odr-use(6.2),不是由a的初始化直接或间接引起的 非本地静态或线程存储持续时间变量。
4实现定义是否使用static动态初始化非本地非内联变量 存储持续时间在
main
的第一个语句之前排序或延迟。如果推迟,则强烈推断 在任何非初始化odr使用之前发生的任何非内联函数或非内联变量 与要初始化的变量相同的转换单元.55它是实现定义的线程和 在程序中哪些点发生延迟动态初始化。 [注意:这些要点应该是 选择的方式允许程序员避免死锁。 - 尾注] [例子:
// - File 1 -
#include "a.h"
#include "b.h"
B b;
A::A(){
b.Use();
}
// - File 2 -
#include "a.h"
A a;
// - File 3 -
#include "a.h"
#include "b.h"
extern A a;
extern B b;
int main() {
a.Use();
b.Use();
}
执行定义是在
a
输入之前初始化b
还是main
还是 初始化会延迟到a
中main
首次使用a
。特别是,如果在main
之前初始化b
输入后,无法保证a
在A::A
初始化之前被初始化,即 在调用a
之前。但是,如果在main
的第一个语句之后的某个时刻初始化了b
,A::A
将会 在{{1}}中使用之前初始化。 - 例子]55)在这种情况下,初始化具有静态存储持续时间且具有副作用的初始化的非局部变量,即使它是 本身并没有使用(6.2,6.6.4.1)。