变量模板和std :: cout - 构造顺序

Fro*_*art 23 c++ c++11 c++14 c++17

看起来我们可以安全地std::cout在具有静态存储持续时间的对象的构造函数中使用对象,如本问题中所述.

但是,我不完全确定在变量模板的情况下我们可以安全地使用它们:

#include <iostream>

template<class T>
T x = T{};

void foo()
{
    class Test
    {
    public:
        Test() { std::cout << "Test::Test\n"; }
    };

    Test t = x<Test>;
}


int main()
{
    std::cout << "main\n";
}
Run Code Online (Sandbox Code Playgroud)

这段代码在clang(实例)崩溃,我不确定它是否是一个bug.

T.C*_*.C. 21

正如那个问题所解释的,一个效果

#include <iostream>
Run Code Online (Sandbox Code Playgroud)

相当于定义一个全局变量

static std::ios_base::Init __init;
Run Code Online (Sandbox Code Playgroud)

(假设您在TU的开头包含它)保证对于在同一TU中具有有序初始化的所有静态存储持续时间对象,已经设置了流对象.

但是,显式和隐式实例化的模板特化具有无序初始化([basic.start.dynamic]/1)1:

如果变量是隐式或显式实例化的特化,则静态存储持续时间的非局部变量的动态初始化是无序的,否则是有序的[ 注释省略 ].在单个翻译单元中定义的具有有序初始化的变量应按其在翻译单元中的定义的顺序进行初始化.

从那以后

如果程序启动一个线程,则对于每个其他动态初始化,后续无序的变量初始化都是无序的.否则,对于每个其他动态初始化,变量的无序初始化是不确定地排序的.

在初始化变量模板特化时,无法保证流对象已x<Test>初始化.

在这种情况下,由于其中一个可能的执行会导致未定义的行为(在初始化之前使用流对象),整个程序的行为是未定义的(请参阅[intro.execution]/5).

修复是在构造std::ios_base::Init函数中自己构造一个对象Test.


1当C++ 14发布时,实际上对于变量模板没有规定,但它始终是意图.

  • @FrozenHeart是的.不管怎样,不同的TU之间没有订购. (2认同)