Ch7.5: constinit

Overview

constinit guarantees that a variable is initialized at compile time, but unlike constexpr, the variable itself does not need to be a constant expression. It simply ensures that the initialization happens before any dynamic initialization.

This is extremely useful for preventing static initialization order problems.

1. Basic example

A constinit variable must be initialized with a constant initializer, but the variable itself does not need to be constexpr.


#include 

constinit int counter = 0;   // guaranteed compile-time initialization

int main()
{
    using namespace ::fast_io::iomnp;

    ++counter;                // OK: not constexpr
    print("counter = ", counter, "\n");
}

The initialization is compile‑time, but the variable is mutable at runtime.

2. Why this is useful

constinit solves a real problem: the static initialization order fiasco. It ensures that a global variable is initialized before any dynamic initialization begins.

Without constinit, two global variables in different translation units may be initialized in an unpredictable order.

3. Preventing static initialization order issues

Consider two global variables in different files:


// file A
#include 

constinit int global_value = 42;

// file B
extern int global_value;

int compute()
{
    return global_value * 2;   // safe: global_value is initialized early
}

Because global_value is constinit, it is guaranteed to be initialized before compute() runs.

4. Difference from constexpr

constexpr means:

constinit means:


constexpr int a = 5;     // constant expression, immutable
constinit int b = 5;     // compile-time init, mutable

b can be changed at runtime; a cannot.

5. constinit does not force constant evaluation

constinit only requires that the initializer is a constant initializer. It does not force the variable to be used in constant expressions.


constinit int x = 10;   // must be initialized at compile time
int y = x + 5;          // runtime use is fine

This makes constinit ideal for global state that must be initialized early but remains mutable.

Key takeaways