Подтвердить что ты не робот

Переменные шаблоны и std:: cout - порядок построения

Похоже, мы можем безопасно использовать объект 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 (живой пример), и я не уверен, что это ошибка или нет.

4b9b3361

Ответ 1

Как объясняется в этом вопросе, один эффект

#include <iostream>

является эквивалентом определения глобальной переменной

static std::ios_base::Init __init;

который (при условии, что вы включите его в начале TU) гарантирует, что для всех объектов статической продолжительности хранения с упорядоченной инициализацией в одном и том же TU объекты потока были настроены.

Однако явно и неявно созданные экземпляры шаблонов имеют неупорядоченную инициализацию ([basic.start.dynamic]/1) 1:

Динамическая инициализация нелокальной переменной со статическим хранилищем длительность неупорядочена, если переменная неявно или явно экземплярная специализация, а в противном случае - [примечание опущено]. Переменные с упорядоченной инициализацией, определенные в одной единице перевода, должны быть инициализированы в порядке их определений в блоке перевода.

А поскольку

Если программа запускает поток, последующая неупорядоченная инициализация переменной не зависит от каждой другой динамической инициализация. В противном случае неупорядоченная инициализация переменной неопределенно упорядочена по отношению ко всем другим динамическим инициализации.

нет гарантии, что объекты потока были инициализированы в момент инициализации специализации шаблона переменных x<Test>.

В этом случае, поскольку одно из возможных исполнений приводит к поведению undefined (с использованием объектов потока до их инициализации), поведение всей программы - undefined (см. [intro.execution]/5).

Исправление состоит в том, чтобы самостоятельно построить объект std::ios_base::Init в конструкторе Test.


1 Это фактически недоказано для переменных шаблонов, когда С++ 14 был опубликован, но это всегда было целью.