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

// File: InitFirst.h

#pragma once

template <int val>
struct InitFirst
{
    static float s_dividedByThree;
};

template <int val>
float InitFirst<val>::s_dividedByThree = val / 3.0;

// File: Test.h

#include <conio.h>
#include <tchar.h>

#include "InitFirst.h"

float g_shouldBeOneThird = InitFirst<1>::s_dividedByThree;

int _tmain(int argc, _TCHAR* argv[])
{
    _cprintf("%f\n", g_shouldBeOneThird);
    getch();
    return 0;
}

Гарантировано ли, что g_shouldBeOneThird будет инициализирован примерно до 0,333? Другими словами, гарантируется ли инициализация статически инициализированного InitFirst‹1>::s_dividedByThree к моменту его использования для статической инициализации g_shouldBeOneThird?


person zeroes00    schedule 04.04.2012    source источник


Ответы (1)


Из стандарта (3.6.2):

Объекты со статической продолжительностью хранения (3.7.1) должны быть инициализированы нулями (8.5) до того, как произойдет любая другая инициализация. Ссылка со статической продолжительностью хранения и объект типа POD со статической продолжительностью хранения могут быть инициализированы константным выражением (5.19); это называется постоянной инициализацией. Вместе инициализация нулями и инициализация констант называются статической инициализацией; вся остальная инициализация является динамической инициализацией. Статическая инициализация должна выполняться до любой динамической инициализации. Динамическая инициализация объекта бывает упорядоченной или неупорядоченной. Определения явно специализированных членов статических данных шаблона класса имеют упорядоченную инициализацию. Другие члены статических данных шаблона класса (т. е. неявно или явно созданные экземпляры специализаций) имеют неупорядоченную инициализацию. Другие объекты, определенные в области пространства имен, имеют упорядоченную инициализацию. Объекты, определенные в одной единице перевода и с упорядоченной инициализацией, должны быть инициализированы в порядке их определения в единице перевода. Порядок инициализации не указан для объектов с неупорядоченной инициализацией и для объектов, определенных в разных единицах перевода.

В вашем случае здесь, поскольку вы инициализируете float InitFirst<val>::s_dividedByThree с постоянным выражением, это произойдет до любой динамической инициализации (f.x float g_shouldBeOneThird). Хотя у меня есть ощущение, что этот упрощенный пример может быть упрощением случая, когда у вас есть динамическая инициализация, тогда соответствующая часть такова: «Объекты, определенные в одной единице перевода и с упорядоченной инициализацией, должны быть инициализированы в порядке их определений в единица перевода.».

Существует хитрость, позволяющая убедиться, что глобальные переменные (вроде) инициализированы к моменту их использования. Хитрость заключается в том, чтобы сохранить их как статическую локальную переменную в глобальной функции. Поскольку локальные статические переменные инициализируются при первом доступе к ним, порядок инициализации больше не является проблемой:

template <int val>
struct InitFirst
{
    static float & s_dividedByThree();
};

template <int val>
float & InitFirst<val>::s_dividedByThree(){
    static float staticVariable = val / 3.0;
    return staticVariable;
}

Затем вы можете получить доступ к этим переменным почти, как и раньше:

float g_shouldBeOneThird = InitFirst<1>::s_dividedByThree();

Однако будьте осторожны, инициализация локальных статических переменных небезопасна в нескольких потоках (в стандарте не указано, что они должны быть безопасными). Если вас это беспокоит, вы можете защитить инициализацию некоторыми блокировками. Компиляторам, конечно, разрешено генерировать безопасный код, что gcc делает по умолчанию (возможно, и другие).

person enobayram    schedule 04.04.2012
comment
Я не голосовал против, но я считаю, что последняя часть неверна (начиная с Однако), и именно это удерживает меня от голосования... - person David Rodríguez - dribeas; 04.04.2012
comment
@DavidRodríguez-dribeas Вы абсолютно правы, я всегда думал иначе. Это также объясняет, почему статические члены классов шаблонов имеют неупорядоченную инициализацию. Компоновщик может разместить их в любом месте при сворачивании экземпляров шаблона. - person enobayram; 04.04.2012
comment
@enobayram Вы правильно догадались, мой фактический код не использует константные выражения для инициализации данных, поэтому инициализация будет динамической. И если я правильно соображу, порядок инициализации и, следовательно, поведение моей программы будут неопределенными. - person zeroes00; 04.04.2012