Из стандарта (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