Early initialization of static local variables.

I had this posted as request for clarification in another thread, but it deserves a thread of its own, I think.

The question is basically - under what circumstances the compiler can perform early initialization of static local variables. I though that the perceived order of initialization (from the programmer's perspective) is never influenced by the actual order of initialization that the compiler chooses. But certain posts over the internet left me with a doubt.

6.7/4:
... An implementation is permitted to perform early initialization of other local objects with static storage duration under the same conditions that an implementation is permitted to statically initialize an object with static storage duration in namespace scope (3.6.2). Otherwise ...

So off we go to 3.6.2.

3.6.2/1:
... Zero-initialization and initialization with a constant expression are collectively called static initialization; all other initialization is dynamic initialization. Objects of POD types (3.9) with static storage duration initialized with constant expressions (5.19) shall be initialized before any dynamic initialization takes place. ...

I understand that certain forms of initializing expressions are called static initialization, and some dynamic initialization. I am not certain what is the meaning of constant expression in this context. Specifically, look at my example at the bottom.

3.6.2/2 (part 1):
An implementation is permitted to perform the initialization of an object of namespace scope with static storage duration as a static initialization even if such initialization is not required to be done statically, provided that

Was the term "static initialization" used in a different sense in the previous paragraph? Is it a kind of initializing expression or variety of initialization procedure?

3.6.2/2 (part 2):
— the dynamic version of the initialization does not change the value of any other object of namespace scope with static storage duration prior to its initialization, and

Well, is it just me (be kind :) or "the initialization...prior to its initialization" expression is oxymoron. Does "the initialization" include the construction of the object or only the calculation of the initializer expression (construction arguments)? My point is, are side-effects from the constructor considered in this clause?

3.6.2/2 (part 3):
— the static version of the initialization produces the same value in the initialized object as would be produced by the dynamic initialization if all objects not required to be initialized statically were initialized dynamically

The same as above. What about side effects inside the constructor?

In fact, the compiler implementer can always change any behavior as he chooses, as long as there is no significant (say performance-wise) perceived effect. This is implicit. Is there anything that this clause states, besides reassert this fact?

For example, in the following code:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
bool flag;

int f()
{
    if (!flag) flag = true;
    return 5;
}

struct S {
    S(int)
    {
        if (!flag) flag = true;
    }
};

int main()
{
    static S s1 = 5; //is this constant initializing expression?
    static S s2(5); //is this constant initializing expression?
    static S s3 = S(5); //is this constant initializing expression?
    static S s4 = f();
}
I assume that the perceived order and actual order in which those initializations are performed is the same, because they are (contrary to good practice) highly dependent on global data and modify global data.

Is there any doubt regarding the order in which those initializations are performed/attempted? Can they be performed before main (with the OS rebooting respectively)? Which of those are considered "initalized with constant expressions"?

Thanks
Last edited on
Lines 18 and 19 are the same. Any initialization that must execute code in order to complete would fall under dynamic initialization. So if S' constructor or one of its bases had any code in it or if S had a data member with a default constructor that was non-empty, it would definitely fall under dynamic initialization. Since it doesn't, the compiler can treat it either way; doesn't really matter to you. Having said that, line 20 falls in the same category: the compiler _may_ be smart enough to know there are no side effects of f() and thus treat line 20 as constant initialization. But again, it doesn't matter to you.

In short: either they all are constant or they all aren't, which means initialization will occur in order of instantiation within the file. Or, maybe the compiler thinks some are constant and some aren't. In the above example, it doesn't matter. As soon as it does matter, the initialization order will be well-defined. (Like for instance if f() called rand(), discarded the result and returned the constant 5, line 20 would automatically have to be initialized last.
— the dynamic version of the initialization does not change the value of any other object of namespace scope with static storage duration prior to its initialization, and

I'm pretty sure that "prior to its initialization" refers to the other object whose value is changed.
So basically it means that if a dynamic initialization does not change the value of an uninitialized object then it may be implemented as static.
Also, I'd guess it makes no difference if the construction or the argument change that other object, as the construction is part of the initialization.

Concerning your code:
Afaik, functions which return a constant value (even if the function itself is constant) do not count as a constant expression (yet). C++0x will introduce the keyword constexpr which will allow such functions to be considered a constant expression, though.
So basically only line 17 features a constant initialization. The others might be treated constant by the compiler and I guess that's where the undetermined behavior comes from.

So this is basically the evil part:
An implementation is permitted to perform the initialization of an object of namespace scope with static storage duration as a static initialization
Thanks. The comments in the body of the costructor and function were supposed to demonstrate that they have side effects. I will make the side effects explicit now.

Second, I am alarmed by the two specific lines:
Zero-initialization and initialization with a constant expression are collectively called static initialization;
What is constant expression here. Line 17 looks like a constant expression to me. But it is equivalent to line 18, which is very similar to line 19 (except the copy construction), and the latter is certainly not constant expression.
— the dynamic version of the initialization does not change the value of any other object of namespace scope with static storage duration prior to its initialization
Indeed, "prior to its initialization" may be applies to other namespace objects, but is this safe at all. You change other objects before initializing them. Is such pigsty even considered standard compliant?

jsmith wrote:
it would definitely fall under dynamic initialization
I meant them to fall under dynamic initialization. I used stupid comments as designation for this. I will edit the post to make it explicit.
Since it doesn't, the compiler can treat it either way; doesn't really matter to you.
First, I am worried that determining which initialization is static depends on the use of the term "constant expression" in the relevant section from the standard. This is one of my concerns. But also, when dynamic initialization can be turned into static initialization? Now, you say - when there is no difference to you. I want that, but I am not sure if the standard's text gives me that. Would the standard even specify something like that - the compiler can change the underlying implementation if that doesn't affect the program? I thought that was settled somewhere in the introduction to the standard already.

Opossum wrote:
So basically only line 17 features a constant initialization. The others might be treated constant by the compiler and I guess that's where the undetermined behavior comes from.
Absolutely. I really would like to know that for sure.
So this is basically the evil part: ...
I thought it wasn't. Now, I need some reassurance.

Thanks again
Topic archived. No new replies allowed.