Is the order in which digits will be printed in the following simple program implementation-defined?
#include <iostream>
struct Foo
{
Foo()
{
std::cout << "1" << std::endl;
}
};
Foo foo;
int main()
{
std::cout << "2" << std::endl;
}
Some wordings from the standard (Dynamic initialization of non-local variables [basic.start.dynamic]/4):
It is implementation-defined whether the dynamic initialization of a non-local non-inline variable with static storage duration is sequenced before the first statement of main
or is deferred. If it is deferred, it strongly happens before any non-initialization odr-use of any non-inline function or non-inline variable defined in the same translation unit as the variable to be initialized.*
...
*) A non-local variable with static storage duration having initialization with side effects is initialized in this case, even if it is not itself odr-used ([basic.def.odr], [basic.stc.static]).
And the main()
function is not odr-used.
The permitted outputs are 1/2, 2, and 2/1.
If initialization of the variable foo
is not deferred, then it is sequenced before the start of main
, so 1
is printed before 2
.
If the initialization is deferred, then the requirement is that initialization of foo
must happen before any (other) odr-use of foo
. It does not say that no initialization must happen if there is no odr-use. The output 2/1 would definitely be very odd for this example (and the output 2 the only one used in practice by implementations which defer initialization), but I don't see anything in the standard that strictly rules it out.
I believe the reason for the wording in the standard is that it allows implementations to use one guard for deferring the initialization of all such variables in a translation unit. If we amend your example like this:
…
Foo foo;
struct Bar {
Bar() { std::cout << "3\n"; }
void Use() {}
} bar;
int main()
{
std::cout << "2" << std::endl;
bar.Use();
}
With a single guard and deferred initialization, foo
would be initialized along with bar
, even though there is no odr-use (besides initialization) of foo
in the program. In this case, it is also required for consistency because the example uses ordered initialization, so the initialization of foo
must be sequenced before bar
, so the only permitted outputs are 1/3/2 (no deferred initialization) and 2/1/3 (deferred initialization). But if we used a different construct to obtain unordered initialization, an implementation might also produce 2/3/1
(again, with no non-initialization odr-use of foo
).