Following code
#include <iostream>
struct A {
A() {
std::cout << std::endl;
}
};
struct B {
static inline A a;
};
int main() {
}
succeeds after compiling with gcc, but crashes with segmentation fault after compiling with clang. Is the code not standard or is clang wrong?
Cppreference on std::ios_base::Init
reads:
The header
<iostream>
behaves as if it defines (directly or indirectly) an instance ofstd::ios_base::Init
with static storage duration: this makes it safe to access the standard I/O streams in the constructors and destructors of static objects with ordered initialization (as long as#include <iostream>
is included in the translation unit before these objects were defined).
You do include <iostream>
before B::a
, but the initialization of B::a
(with B::a
being static inline
variable) is not part of ordered initialization, so it can be initialized before std::ios_base::Init
. It seems that Clang (some versions, at least) does exactly this. This is a valid behaviour.
The standard reads ([basic.start.dynamic]):
- Dynamic initialization of a non-local variable with static storage duration is unordered if the variable is an implicitly or explicitly instantiated specialization, is partially-ordered if the variable is an inline variable that is not an implicitly or explicitly instantiated specialization, and otherwise is ordered.
So, an initialization of instance of std::ios_base::Init
is ordered, and initialization of B::a
is partially-ordered.
- Dynamic initialization of non-local variables
V
andW
with static storage duration are ordered as follows:3.1. If
V
andW
have ordered initialization and the definition ofV
is appearance-ordered before the definition ofW
, or ifV
has partially-ordered initialization,W
does not have unordered initialization, and for every definitionE
ofW
there exists a definitionD
ofV
such thatD
is appearance-ordered beforeE
, then ...3.2. Otherwise, if the program starts a thread other than the main thread before either
V
orW
is initialized, it is unspecified in which threads the initializations ofV
andW
occur; the initializations are unsequenced if they occur in the same thread.3.3. Otherwise, the initializations of
V
andW
are indeterminately sequenced.
3.1 and 3.2 don't apply. So we have indeterminately sequenced initializations.
You can make B::a
a non-inline
static variable or somehow force std::ios_base::Init
initialization before using std::cout
, for example:
struct A {
A() {
std::cout << std::endl;
}
std::ios_base::Init init;
};