类模板内静态数据的初始化顺序

// 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
", g_shouldBeOneThird);
    getch();
    return 0;
}

g_shouldBeOneThird 是否保证初始化为 0.333 左右?换句话说,静态初始化的 InitFirst<1>::s_dividedByThree 是否保证在用于静态初始化 g_shouldBeOneThird 时被初始化?

Is g_shouldBeOneThird guaranteed to be initialized to around 0.333? In other words, is the statically initialized InitFirst<1>::s_dividedByThree guaranteed to be initialized by the time it's used for statically initializing g_shouldBeOneThird?

推荐答案

来自标准(3.6.2):

From the standard (3.6.2):

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

Objects with static storage duration (3.7.1) shall be zero-initialized (8.5) before any other initialization takes place. A reference with static storage duration and an object of POD type with static storage duration can be initialized with a constant expression (5.19); this is called constant initialization. Together, zero-initialization and constant initialization are called static initialization; all other initialization is dynamic initialization. Static initialization shall be performed before any dynamic initialization takes place. Dynamic initialization of an object is either ordered or unordered. Definitions of explicitly specialized class template static data members have ordered initialization. Other class template static data members (i.e., implicitly or explicitly instantiated specializations) have unordered initialization. Other objects defined in namespace scope have ordered initialization. Objects defined within a single translation unit and with ordered initialization shall be initialized in the order of their definitions in the translation unit. The order of initialization is unspecified for objects with unordered initialization and for objects defined in different translation units.

在您的情况下,由于您使用常量表达式初始化 float InitFirst<val>::s_dividedByThree,这将在任何动态初始化之前发生(fx float g_shouldBeOneThird).虽然我感觉这个简化的示例可能是对您进行动态初始化的情况的简化,但相关部分是:在单个翻译单元中定义并具有有序初始化的对象应按照它们在翻译单元.".

In your case here, since you're initializing float InitFirst<val>::s_dividedByThree with a constant expression, this will happen before any dynamic initialization (f.x float g_shouldBeOneThird). Though I have a feeling this simplified example might be a simplification of a case where you have dynamic initialization, then the relevant part is: "Objects defined within a single translation unit and with ordered initialization shall be initialized in the order of their definitions in the translation unit.".

有一个技巧可以确保全局变量(在某种程度上)在您使用它们时被初始化.诀窍是将它们保留为全局函数中的静态局部变量.由于局部静态变量在第一次被访问时被初始化,初始化顺序不再是问题:

There is a trick to make sure that global variables (sort of) are initialised by the time you use them. The trick is to keep them as a static local variable in a global function. Since local static variables are initialised the first time they're accessed, initialisation order is not an issue anymore:

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;
}

然后您可以像以前一样几乎访问这些变量:

You can then access those variables almost as before:

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

但请注意,局部静态变量的初始化在多线程下是不安全的(在标准中它们不应该是安全的).如果这对您来说是一个问题,您可能希望使用以下方法保护初始化一些锁.编译器当然可以生成安全代码,这是 gcc 默认所做的(可能其他人也是).

Beware though, initialization of local static variables are not safe under multiple threads (it's not in the standard that they should be safe). If that is a concern for you, you might want to protect the initializations with some locks. The compilers are of course allowed to generate safe code, which is what gcc does by default (probably others too).

相关文章