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

时间:2016-06-08 14:50:07

标签: c++ c++11 c++14 c++17

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

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

#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";
}

此代码在clang(live example)中崩溃,我不确定它是否是一个错误。

1 个答案:

答案 0 :(得分:21)

如该问题所述,

的一个效果
#include <iostream>

相当于定义全局变量

static std::ios_base::Init __init;

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

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

  

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

从那以后

  

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

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

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

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

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