One of the ways to avoid expensive object initialization is usage of static local variables. Adding a static keyword to variable declaration is all it takes to make sure that the variable is initialized only once and in a thread-safe way. Unfortunately, as we’ve discussed in one of the older articles
this thread-safety comes at a cost. In practice this means that even a fairly trivial snippet of code like
ends up translated into a significant number of assembly instructions
That’s unfortunate, as it seems like this initialization should be possible at compile time before any code even runs. Wait, if that’s the case, why don’t we give compiler a hint that it’s safe to call Foo’s constructor at compile time using constexpr
Both GCC and Clang immediately get the hint and produce a much nicer
Success! There is one gotcha though - compilers are not required to do this, so what can we do if we really need this optimization? If our variable were a constant, we could use constexpr, but in our case we do want our variable to be mutable, so no luck. Fortunately, C++ 20 has a solution - constinit. Its usage is similar to constexpr, but despite having the same prefix const, constinit makes it possible to declare non-const variables with static or thread-local storage duration.
It’s interesting to look at assembly produced by compilers when non-trivial members are initialized at compile time. For instance, let’s initialize our data_ array with a sequence of numbers 0..n-1
C++ has an impressive number of ways to offload runtime computations to compile-time, so by strategically using them, we can significantly improve user experience and reduce runtime overhead.