Why do global inline variables and static inline members in C++17 need guards?
Asked Answered
M

1

26

Since C++17 it's possible to initialize global variables and static members in headers using the inline keyword. While I understand why static variables in functions need to be guarded (because initialization should happen only once even in a multithreaded context), I don't get why these new inline variables are also guarded (you can see it here: https://godbolt.org/z/YF8PeQ). I thought that in any case initialization of all globals and static members happens at the beginning of program execution (even before main()), so there's no need to think about multiple threads at this moment. Can you explain it, please?

Mcevoy answered 27/6, 2019 at 21:2 Comment(0)
S
26

Every file that contains the definition and uses it will try to initialize the variable. Even if that happens serially, not concurrently, you still need a way to mark the variable as initialized, so that only the first ocurrence will initialize it and later attempts to initialize it won't do anything.

Also, you can have multiple threads before main starts. Constructors of global variables (and functions called by those constructors) can spawn new threads.

So you can have multiple pieces of code, all executing before main, all trying to initialize the same variable. That's what the guards are for.

Silvereye answered 27/6, 2019 at 21:10 Comment(4)
What do you mean by "file will try to initialize"? I thought the fact of initialization should be resolved by linker, and I don't really see why linker can't "combine" all initializations of global variable/static member in different object files into one (like he does with inline global function definitions in header) and put it in the right place in the assembly. And how code in these threads can possibly initialize something except variable which constructor spawned thread of? It can access another global (initialized or not, depends on order), but it can't influence its initialization.Mcevoy
The linker cannot run constructors for global variables that require non-trivial initialization, that has to happen at runtime, after the program starts executing (but before main starts).Silvereye
A global constructor can create a thread, which can call a function defined in a different object file, which can trigger initialization of globals defined in that file. C++ initialization is a lot more complicated than I think you realize.Silvereye
@JohnLettehw - Additionally, a thread running before main() starts can change the value of a global variable. A constructor that runs before main() can read the value of a global variable. Therefor, whether or not the thread does this before the constructor runs is important.Chenay

© 2022 - 2024 — McMap. All rights reserved.