静态存储持续时间初始化

Static-storage-duration initialization

在 C++ 中,静态存储持续时间对象以未指定的顺序初始化(除了在同一编译单元中)。

代码如下:

#include <iostream>

struct Foo {
    Foo() {
        std::cout << "Hello, world.\n";
    }
} foo_instance;

int main(int argc, const char *argv[]) {
    return 0;
}

标准中哪里有说明我已经可以在 foo_instance 的初始化过程中使用 std::cout

我知道我可以通过在 <iostream> 中添加一些技巧来确保事情正常进行,例如让它包含类似

的内容
int __ensure_stdout_initialization_call();
namespace {
    int __ensure_stdout_initialization
      = __ensure_stdout_initialization_call();
}

然而,问题是关于在何处保证为标准库所需的所有初始化完成此操作。

我不确定是否在标准中明确说明(*),但通常是std::cinstd::coutstd::cerr 是在 Nifty Counter 习语的帮助下实现的。

基本思想是在头文件中包含一个辅助静态对象,它在初始化期间检查流对象是否已经初始化,如果没有,则对其进行初始化。由于包含通常先出现,此类辅助静态对象在同一翻译单元中的任何其他静态对象之前被初始化,并确保在任何其他静态对象引用它之前正确初始化流对象。

(*) 编辑:

这是标准草案 N3936 中的适当措辞:

27.4 Standard iostream objects

27.4.1.2

The objects are constructed and the associations are established at some time prior to or during the first time an object of class ios_base::Init is constructed, and in any case before the body of main begins execution. The objects are not destroyed during program execution. The results of including < iostream > in a translation unit shall be as if defined an instance of ios_base::Init with static storage duration. Similarly, the entire program shall behave as if there were at least one instance of ios_base::Init with static storage duration.

看看 header <iostream>.

标准说它的行为就像它定义了类型 std::ios_base::Init 的 TU-local object,它处理初始化并在结束时刷新标准流。

tl;博士;在 foo_instance.

初始化期间不应使用 std::cout

不管标准中标准流的初始化如何,唯一的要求是

27.4.1 Overview [iostream.objects.overview]

3 The objects are constructed and the associations are established at some time prior to or during the first time an object of class ios_base::Init is constructed, and in any case before the body of main begins execution. 291 The objects are not destroyed during program execution. 292 The results of including <iostream> in a translation unit shall be as if <iostream> defined an instance of ios_base::Init with static storage duration.

因此,如果您在声明静态变量之前包含 <iostream>,您就得救了,因为根据标准

3.6.3 Dynamic initialization of non-local variables [basic.start.dynamic]

2 Dynamic initialization of non-local variables V and W with static storage duration are ordered as follows: (2.1) If V and W have ordered initialization and V is defined before W within a single translation unit, the initialization of V is sequenced before the initialization of W.

所以 ios_base::Init 将在您的变量和标准流准备就绪之前初始化,但如果您在 [=36] 之前声明变量,您似乎仍然可以开枪打自己的腿=] 包括 <iostream>:

struct Foo
{
    Foo();
} foo_instance; // uses ::std::cout

#include <iostream> // declares ios_base::Init variable that will init ::std::cout

Foo::Foo()
{
    std::cout << "Hello, world.\n";
}

int main(int argc, const char *argv[]) {
    return 0;
}

dying example

所以我可以得出结论,你不能在非局部变量的动态初始化期间使用std::cout。