非本地非内联变量的初始化:是否严格在"main()"函数调用之前进行

The initialization of non-local non-inline variables: does it take place strictly before the `main()` function call?

本文关键字:quot main 函数调用 变量 是否 初始化      更新时间:2023-10-16

是否定义了以下简单程序实现中打印数字的顺序?

#include <iostream>

struct Foo
{
Foo()
{
std::cout << "1" << std::endl;
}
};
Foo foo;

int main()
{
std::cout << "2" << std::endl;
}

标准中的一些词语(非局部变量的动态初始化[basic.start.Dynamic]/4):

实现定义了具有静态存储持续时间的非本地非内联变量的动态初始化是在main的第一条语句之前排序还是推迟。如果它被推迟,它强烈发生在任何非初始化或使用任何非内联函数或在与要初始化的变量相同的转换单元中定义的非内联变量之前。*

*)在这种情况下,具有静态存储持续时间的非局部变量被初始化,该变量的初始化具有副作用,即使它本身没有使用odr([basic.def.odr],[basic.stc.static])

并且未使用main()函数。

允许的输出为1/2、2和2/1。

如果变量foo的初始化是而不是延迟的,则它在main开始之前被排序,因此12之前被打印。

如果推迟初始化,则要求foo的初始化必须在任何(其他)odr使用foo之前进行。它并没有说,如果没有odr的使用,就不必进行初始化。对于这个例子来说,输出2/1肯定是非常奇怪的(输出2是延迟初始化的实现在实践中唯一使用的),但我在标准中没有看到任何严格排除它的东西。

我相信标准中措辞的原因是,它允许实现使用一个保护来推迟翻译单元中所有此类变量的初始化。如果我们这样修改您的示例:

…
Foo foo;
struct Bar {
Bar() { std::cout << "3n"; }
void Use() {}
} bar;

int main()
{
std::cout << "2" << std::endl;
bar.Use();
}

通过单个保护和延迟初始化,foo将与bar一起初始化,即使程序中没有对foo的odr使用(除了初始化)。在这种情况下,它也是一致性所必需的,因为该示例使用有序初始化,因此foo的初始化必须在bar之前排序,因此唯一允许的输出是1/3/2(无延迟初始化)和2/1/3(延迟初始化)。但是,如果我们使用不同的构造来获得无序初始化,那么一个实现也可能产生2/3/1(同样,没有使用foo的非初始化)。