C.h:
#include <iostream>
class C {
public:
explicit C(int id) { std::cout<<"Initialized "<<id<<"\n"; }
};
1.cpp:
#include "C.h"
C global(1);
2.cpp:
#include "C.h"
thread_local C thread(2);
int main() {}
My question is: Is it guaranteed that global
will be initialized before thread
?
The C++ standard is somewhat vague on this point, as far as I understand it. It says (from the C++17 n4659 draft):
[basic.start.static] Static initialization
Variables with static storage duration are initialized as a consequence of program initiation. Variables with thread storage duration are initialized as a consequence of thread execution.
It stands to reason that "program initiation" happen before "thread execution", but since both those expressions appear in the standard only in that place, I'm seeking advise from actual language lawyers.
I'm going to use the C++20 working draft since the wording there is a little cleaner, although none of the real rules have changed.
First, thread_local
behaves basically like static
as far as non-local goes: [basic.stc.thread]/2:
[ Note: A variable with thread storage duration is initialized as specified in [basic.start.static], [basic.start.dynamic], and [stmt.dcl] and, if constructed, is destroyed on thread exit ([basic.start.term]). — end note ]
Yes, it's a note. But a non-local object declared thread_local
is basically static
so this makes sense.
Now, neither global
nor thread
have constant initialization - so both are zero initialized and then they have to undergo dynamic initialization. To [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.
Neither of our variables are specializations, neither of them are inline. So both are ordered.
A declaration D is appearance-ordered before a declaration E if
- D appears in the same translation unit as E, or
- the translation unit containing E has an interface dependency on the translation unit containing D,
in either case prior to E.
Our declarations are not appearance-ordered with respect to each other.
Dynamic initialization of non-local variables V and W with static storage duration are ordered as follows:
Alright, sub-bullet 1:
If V and W have ordered initialization and the definition of V is appearance-ordered before the definition of W, or if V has partially-ordered initialization, W does not have unordered initialization, and for every definition E of W there exists a definition D of V such that D is appearance-ordered before E,
Doesn't apply. It's a complicated condition, but it doesn't apply.
Otherwise, if the program starts a thread other than the main thread before either V or W is initialized, it is unspecified in which threads the initializations of V and W occur; the initializations are unsequenced if they occur in the same thread.
Nope, no threads.
Otherwise, the initializations of V and W are indeterminately sequenced.
There we go. global
and thread
are indeterminately sequenced.
Note also that:
It is implementation-defined whether the dynamic initialization of a non-local inline variable with static storage duration is sequenced before the first statement of main or is deferred.
and:
It is implementation-defined whether the dynamic initialization of a non-local non-inline variable with thread storage duration is sequenced before the first statement of the initial function of a thread or is deferred.
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