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).
与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…