【问题标题】:Static-storage-duration initialization静态存储持续时间初始化
【发布时间】:2017-06-18 08:24:14
【问题描述】:

在 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 的标准中说明了哪里?

我知道我可以通过在 &lt;iostream&gt; 中添加一些技巧来确保一切正常,例如让它包含类似的内容

int __ensure_stdout_initialization_call();
namespace {
    int __ensure_stdout_initialization
      = __ensure_stdout_initialization_call();
}

然而,问题是在哪里可以保证标准库所需的所有初始化都完成了。

【问题讨论】:

    标签: c++ initialization language-lawyer iostream


    【解决方案1】:

    tl;博士;在foo_instance 的初始化过程中不应使用std::cout

    无论在标准中初始化标准流,唯一的要求是

    27.4.1 概述 [iostream.objects.overview]

    3 在第一次构造ios_base::Init 类的对象之前或期间的某个时间构造对象并建立关联,并且无论如何在main 的主体开始执行之前。 291 程序执行期间对象未销毁。 292 在翻译单元中包含&lt;iostream&gt; 的结果应如同&lt;iostream&gt; 定义了具有静态存储持续时间的ios_base::Init 的实例。

    因此,如果您在声明静态变量之前包含&lt;iostream&gt;,则可以保存,因为根据标准

    3.6.3 非局部变量的动态初始化[basic.start.dynamic]

    2 具有静态存储时长的非局部变量 V 和 W 的动态初始化顺序如下: (2.1) 如果V和W已经有序初始化并且在单个翻译单元中V在W之前定义,则V的初始化在W的初始化之前排序。

    所以ios_base::Init 将在你的变量和标准流准备好之前被初始化,但如果你声明你的变量before 包括&lt;iostream&gt;,你似乎仍然可以在腿上开枪:

    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。

    【讨论】:

    • 好点。更改了已接受的答案,因为在提供参考的同时显示了一个漏洞。
    【解决方案2】:

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

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

    (*) 编辑:

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

    27.4 标准 iostream 对象

    27.4.1.2

    对象被构造并建立关联 在第一次类对象之前或期间的某个时间 ios_base::Init 被构造,并且无论如何在 main 的主体之前 开始执行。程序期间对象不被破坏 执行。在翻译单元中包含 的结果 就像定义了一个 ios_base::Init 的实例一样 静态存储时间。同样,整个程序应表现为 如果至少有一个带有静态的 ios_base::Init 实例 存储期限。

    【讨论】:

      【解决方案3】:

      看看标题&lt;iostream&gt;

      标准说它定义了一个 std::ios_base::Init 类型的 TU 本地对象,它处理初始化并在最后刷新标准流。

      【讨论】:

        猜你喜欢
        • 2016-11-06
        • 2020-02-17
        • 2019-02-11
        • 1970-01-01
        • 2021-08-20
        • 2021-02-08
        • 2011-09-09
        • 2017-05-26
        • 2015-10-24
        相关资源
        最近更新 更多