问题
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?
https://godbolt.org/z/tEvfrW
回答1:
Cppreference on std::ios_base::Init reads:
The header
<iostream>behaves as if it defines (directly or indirectly) an instance ofstd::ios_base::Initwith 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
VandWwith static storage duration are ordered as follows:3.1. If
VandWhave ordered initialization and the definition ofVis appearance-ordered before the definition ofW, or ifVhas partially-ordered initialization,Wdoes not have unordered initialization, and for every definitionEofWthere exists a definitionDofVsuch thatDis appearance-ordered beforeE, then ...3.2. Otherwise, if the program starts a thread other than the main thread before either
VorWis initialized, it is unspecified in which threads the initializations ofVandWoccur; the initializations are unsequenced if they occur in the same thread.3.3. Otherwise, the initializations of
VandWare 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;
};
来源:https://stackoverflow.com/questions/62738001/using-cout-in-the-constructor-of-a-class-that-is-included-in-another-class-as-a