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

Variable templates and std::cout -- order of construction

看起来我们可以安全地在具有静态存储持续时间的对象的构造函数中使用 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) 中崩溃,我不确定它是否是错误。

如该问题所述,

的一个影响
#include <iostream>

相当于定义一个全局变量

static std::ios_base::Init __init;

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

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

Dynamic initialization of a non-local variable with static storage duration is unordered if the variable is an implicitly or explicitly instantiated specialization, and otherwise is ordered [note omitted]. Variables with ordered initialization defined within a single translation unit shall be initialized in the order of their definitions in the translation unit.

自从

If a program starts a thread, the subsequent unordered initialization of a variable is unsequenced with respect to every other dynamic initialization. Otherwise, the unordered initialization of a variable is indeterminately sequenced with respect to every other dynamic initialization.

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

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

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


1 这实际上在 C++14 发布时没有为变量模板指定,但这一直是意图。