在作为静态成员包含在另一个类中的类的构造函数中使用 cout

til*_*lin 8 c++ clang initialization-order language-lawyer

以下代码

#include <iostream>

struct A {
    A() {
        std::cout << std::endl;
    }
};

struct B {
    static inline A a;
};

int main() {
}
Run Code Online (Sandbox Code Playgroud)

用gcc 编译后成功,但用clang 编译后因分段错误而崩溃。代码不标准还是叮当错了?

https://godbolt.org/z/tEvfrW

Evg*_*Evg 7

Cppreferencestd::ios_base::Init 读取

标头的<iostream>行为就像它定义(直接或间接)一个std::ios_base::Init具有静态存储持续时间的实例:这使得访问具有有序初始化的静态对象的构造函数和析构函数中的标准 I/O 流变得安全(只要#include <iostream>包含在定义这些对象之前的翻译单元)。

你包括<iostream>B::a,但初始化B::a(带B::astatic inline可变的)不是的一部分下令初始化,所以它可以之前被初始化std::ios_base::Init。似乎 Clang(至少在某些版本中)正是这样做的。这是一个有效的行为。

标准读取([basic.start.dynamic]):

  1. 如果变量是隐式或显式实例化的特化,则具有静态存储持续时间的非局部变量的动态初始化是无序的,如果变量是不是隐式或显式实例化的特化的内联变量,则是部分有序的,否则是有序的.

所以,instance of 的初始化std::ios_base::Init是有序的,而 of 的初始化B::a是部分有序的。

  1. 非局部变量的动态初始化VW静态存储持续时间的顺序如下:

3.1. 如果VW已经有序初始化并且 的定义V在 的定义之前是外观有序的W,或者如果V具有偏序初始化,W没有无序初始化,并且对于 的每个定义EW存在一个这样的定义D,在 之前是外观有序的,然后 ...VDE

3.2. 否则,如果程序在初始化VW初始化之前启动了主线程以外的线程,则未指定初始化VW发生在哪个线程中;如果它们发生在同一线程中,则初始化是无序的。

3.3. 否则的初始化VW被测序不定。

3.1 和 3.2 不适用。所以我们有不确定的初始化顺序。

您可以在使用之前创建B::a一个非inline静态变量或以某种方式强制std::ios_base::Init初始化std::cout例如

struct A {
    A() {
        std::cout << std::endl;
    }

    std::ios_base::Init init;
};
Run Code Online (Sandbox Code Playgroud)