3
// 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 时被初始化?

4

1 回答 1

3

从标准(3.6.2):

具有静态存储持续时间(3.7.1)的对象应在任何其他初始化发生之前进行零初始化(8.5)。具有静态存储持续时间的引用和具有静态存储持续时间的 POD 类型的对象可以使用常量表达式(5.19)进行初始化;这称为常量初始化。零初始化和常量初始化合称为静态初始化;所有其他初始化都是动态初始化。静态初始化应在任何动态初始化发生之前执行。对象的动态初始化是有序的或无序的。显式专门化的类模板静态数据成员的定义已排序初始化。其他类模板静态数据成员(即,隐式或显式实例化的特化)具有无序初始化。命名空间范围内定义的其他对象已按顺序初始化。在单个翻译单元中定义并具有有序初始化的对象应按照它们在翻译单元中的定义顺序进行初始化。对于具有无序初始化的对象和在不同翻译单元中定义的对象,初始化的顺序是未指定的。

在您的情况下,由于您使用float InitFirst<val>::s_dividedByThree常量表达式进行初始化,因此这将在任何动态初始化(fx 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 默认所做的(可能其他人也是如此)。

于 2012-04-04T12:31:00.600 回答