Порядок инициализации статических элементов шаблона

У меня есть вопрос, связанный с предыдущим вопросом, размещенным здесь Порядок инициализации статического поля Предположим, у меня есть следующая структура с 2 статическими членами x и y (сами шаблоны)

#include <iostream>

using namespace std;

template <typename T>
struct Foo
{
    static T x;
    static T y;
    Foo()
    { 
         cout << "x = " << x << endl;
         cout << "y = " << y << endl;
    }
};

template <typename T>
T Foo<T>::x = 1.1f;

template <typename T>
T Foo<T>::y = 2.0 * Foo<T>::x;


int main()
{
    Foo<double> foo;
}

Выход:

x = 1.1 
y = 2.2

Я инициализирую x и y выше main(), и вы можете видеть, что y зависит от x, поэтому лучше сначала инициализировать x.

Мои вопросы:

  1. В момент инициализации типы x и y еще неизвестны, так когда же они действительно инициализируются? Действительно ли статические члены инициализируются после создания экземпляра шаблона Foo<double> foo; в main()?
  2. И если да, то порядок объявлений x и y, кажется, не имеет значения, т.е. я могу сначала объявить y, затем x (как в структуре, так и в статической инициализации) и все равно получить правильный результат, т.е. компилятор каким-то образом знает, что y зависит от x. Это четко определенное поведение (т.е. соответствует стандартам)? Я использую g ++ 4.8 и clang ++ в OS X.

Спасибо!


person vsoftco    schedule 24.05.2014    source источник
comment
Я удалил свой ответ, вам, вероятно, потребуется кто-то с более стандартными знаниями, извините!   -  person user657267    schedule 24.05.2014
comment
@ user657267 Нет проблем, мне очень любопытно узнать ответ, так как я нигде не могу найти удовлетворительный.   -  person vsoftco    schedule 24.05.2014


Ответы (1)


Этот код безопасен, потому что Foo<double>::x имеет постоянную инициализацию, но Foo<double>::y имеет динамическую инициализацию.

3.6.2/2:

Постоянная инициализация выполняется:

  • ...

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

Вместе инициализация нулем и инициализация константой называются статической инициализацией; вся остальная инициализация - это динамическая инициализация. Статическая инициализация должна выполняться до любой динамической инициализации.

С другой стороны, если у вас было:

double tmp = 1.1;

template <typename T>
T Foo<T>::x = tmp;

template <typename T>
T Foo<T>::y = 2.0 * Foo<T>::x;

этот код будет небезопасным - Foo<double>::y может оказаться либо 2.2, либо 0.0 (при условии, что ничто другое не изменяет tmp во время динамических инициализаций).

person aschepler    schedule 24.05.2014
comment
Хорошо, спасибо за разъяснение этой части, +1. Однако как же на самом деле работает линия template <typename T> T Foo<T>::x = 1.1f;? Тип не известен в объявлении, инициализирован ли статический член по умолчанию с помощью float, независимо от T? Или действительно инициализируется только после Foo<double> foo;? Это то, что меня больше всего озадачивает. - person vsoftco; 24.05.2014
comment
Когда компилятор видит Foo<double>, он создает экземпляр класса только для определения имен и типов всех членов класса. Затем Foo<double> foo; использует конструктор по умолчанию, поэтому создается экземпляр определения функции для Foo<double>::Foo(). В этом определении используются элементы x и y, поэтому создаются экземпляры определений элементов для Foo<double>::x и Foo<double>::y. Компилятор устанавливает для них постоянные объекты с начальным значением и / или кодом для инициализации. Инициализация фактически происходит при выполнении программы. - person aschepler; 24.05.2014
comment
Хорошо, теперь это имеет смысл! Спасибо! - person vsoftco; 24.05.2014
comment
Я не думаю, что это правильно: глобальные объекты в одном TU инициализируются в том же порядке, в котором они определены. В приведенном выше коде гарантируется, что порядок инициализации равен tmp, x, y, и, следовательно, y гарантированно равен 2.2. - person Nir Friedman; 19.07.2018
comment
@NirFriedman Для не-шаблонов - да. Но [basic.start.dynamic] / 1 Динамическая инициализация нелокальной переменной со статической продолжительностью хранения неупорядочена, если переменная является неявно или явно конкретизированной специализацией ... что в конечном итоге означает, что порядок инициализации неопределенно упорядочен (или возможно, даже неупорядоченный, если задействованы потоки). - person aschepler; 20.07.2018
comment
Используется ли в стандарте слово «специализация» по-другому? Здесь нет специализации, насколько я понимаю. Использует ли стандарт специализацию просто для ссылки на конкретную реализацию шаблона, даже если существует только первичное определение? Если да, то я отвечу на мой вопрос. - person Nir Friedman; 20.07.2018