Reference:
[C++][C++20] consteval / constexpr
[C++/Rust] use of thread_local in code
[Note] linking notes
Initializing order
- Perform constant initialization and zero initialization
- Perform dynamic initialization
- Start executing main() How main() is executed on Linux
- Perform dynamic initialization of function-local static variables, as needed.
- It is impossible to read uninitialized global memory.
- Destroy function-local static variables that were initialized in reverse order
- Destroy other globals in reverse order.
header.h
tu.cpp
Templated variables are init. at some point before main()
Compiler is allowed to perform dynamic initialization at compile-time.
Compiler is allowed to perform dynamic init. after main() has started executing.
i.e. an unused global variable might not be initialized at all.The static initialization order fiasco
Dynamic initialization order of globals in different translation units is not specified.
GuidelineUnless otherwise allowed by its documentation, do not access global state in the constructor of another global.
* Solution 1
Globals initialized by some constant expression are initialized during static initialization without dynamic initialization.Constant expressions cannot access globals that are dynamically initialized.
Guideline
Whenever possible, make global variables constexpr.
Guideline
Whenever possible, use constant initialization.
e.g.
Guideline
Declare global variables constinit to check for initialization problems.
Whenever possible, make global variables constexpr.
Whenever possible, use constant initialization.
Always look into the code path that has function which is not constexpr.
e.g.C++20
constinit = constexpr - const
- variable is initialized using constant initialization (or error)
- variable is not const
Declare global variables constinit to check for initialization problems.
Guideline
Try to add a constexpr constructor to every type.
Try to add a constexpr constructor to every type.
e.g.
Default constructors of containers(with default allocator)
Default constructors of resource handles(files, sockets, ...)
Default constructors of containers(with default allocator)
Default constructors of resource handles(files, sockets, ...)
Guideline
Do not use constinit if need to do lazy initialization.
Do not use constinit if need to do lazy initialization.
* Solution 2:
Lazy initialization.
Never cache a (reference to) a function-local static in a global variable. logger.h logger.cpp
Global variable destruction
Global variables are destroyed in reverse dynamic initialization order.Beware, constinit always initialized first, and that is its purpose. init. b
init. a
init. c
destroy c
destroy b
destroy a
Destruction order of globals in different TUs is not specified.
GuidelineUnless otherwise allowed by its documentation, do not access global state in the destructor of another global.
This applies to constinit globals.
Rule
header.h a.cpp b.cpp
Use manual initialization either for all globals in your project, or none.
And remember to initialize them all!
- The order of dynamic initialization is not specified.
-
Exception: within one translation unit, variables are initialized from top to bottom.
1) Must include the header that declares the global before using it
2) every global defined in that header is initialized before your global. - Do not use function-local static variables if there's a chance they might be used after main()
- Do not use function-local static variables.
Nifty counters
- Ensure the nifty counter object is included in the definition file.
- The nifty counter approach doesn't work for templated objects.
header.h a.cpp b.cpp
Conclusion
- constinit is not always applicable
- lazy initialization has to leak
- nifty counters are black magic
- Or, simply don't do anything before or after main()
Manual initialization (Google's way)
GuidelineUse manual initialization either for all globals in your project, or none.
And remember to initialize them all!
header.h
main.cpp
No comments:
Post a Comment
Note: Only a member of this blog may post a comment.