类模板中静态数据的初始化顺序 [英] Initialization order of static data inside class template

查看:117
本文介绍了类模板中静态数据的初始化顺序的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

  // 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>

#includeInitFirst.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?
}


$ b <换句话说,是静态初始化的InitFirst< 1> :: s_dividedByThree保证在用于静态初始化g_shouldBeOneThird的时候被初始化?

解决方案

从标准(3.6.2):


具有静态存储持续时间
(8.5),在任何其他初始化发生之前。具有
静态存储持续时间的引用和具有静态存储的POD类型的对象
持续时间可以用常数表达式(5.19)初始化;这是
调用常量初始化。一起,零初始化和
常量初始化称为静态初始化;所有其他
初始化是动态初始化。在任何动态初始化发生之前,必须执行静态初始化
。动态
对象的初始化是有序的或无序的。
explicit特殊类模板的定义static data
成员已经排序初始化。其他类模板静态数据
成员(即隐式或显式实例化的特化)
具有无序的初始化。在命名空间
scope中定义的其他对象有顺序初始化。在单个
翻译单元内定义的并且具有有序初始化的对象将按照它们在翻译单元中的定义的顺序被初始化




初始化的顺序
未初始化的对象的未定义
初始化的对象和未定义
初始化的对象。 >在这种情况下,由于你用一个常量表达式初始化 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默认情况下(也许是其他人)。


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

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?

解决方案

From the standard (3.6.2):

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.

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();

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).

这篇关于类模板中静态数据的初始化顺序的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆