I'd have expected the following code to yield a segmentation fault (or otherwise UB):
struct T {
T();
};
T t;
char const* str = "Test string";
T::T() {
std::cout << str; // zero-initialised, only!
}
int main() {}
That's because t
is initialised before str
. I'd expect str
to hold the value (char const*)0
due to zero-initialisation. My interpretation of [C++11: 3.6.2/2]
supports this.
However, the above snippet appears to output the string as expected (and I confirmed the behaviour by also printing the pointer's value).
Is there some rule of static initialisation that I'm missing here, that allows str
to be value-initialised before t
begins construction? Where is it in the standard?
This came up on static variable resolution at build time, where an answerer asserted that using char const*
rather than std::string
for a static global avoids the static initialisation order fiasco. I disagreed, but now I'm not so sure...
str
is initialized by a constant expression and const char *
is a POD type (C++03 terms, but C++11 it is analogous, but with different terms and way more allowed cases). Such an initialization is done in static initialization phase, and the static initialization phase has no issue of order. It happens before any dynamic initialization. t
is initialized in the dynamic initialization phase.
Built-in types aren't initialised at all, in the normal sense. Commonly, their initial contents are memory-mapped directly from a special region of the binary as part of loading it.
I think I found it; what's happening here is not so much about the built-in type, but about the constant initialiser:
[C++11: 3.6.2/2]:
Variables with static storage duration (3.7.1) or thread storage duration (3.7.2) shall be zero-initialized (8.5) before any other initialization takes place.Constant initialization is performed:
- if each full-expression (including implicit conversions) that appears in the initializer of a reference with static or thread storage duration is a constant expression (5.19) and the reference is bound to an lvalue designating an object with static storage duration or to a temporary (see 12.2);
- if an object with static or thread storage duration is initialized by a constructor call, if the constructor is a
constexpr
constructor, if all constructor arguments are constant expressions (including conversions), and if, after function invocation substitution (7.1.5), every constructor call and full-expression in the mem-initializers and in the brace-or-equal-initializers for non-static data members is a constant expression;- if an object with static or thread storage duration is not initialized by a constructor call and if every full-expression that appears in its initializer is a constant expression.
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. [..]
That final sentence would seem to override subsequent sequencing rules, making this ordering apply across Translation Units.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With