Skip to main content

Documentation Index

Fetch the complete documentation index at: https://docs.syntblaze.com/llms.txt

Use this file to discover all available pages before exploring further.

The constinit specifier, introduced in C++20, mandates that a variable with static or thread storage duration must be initialized at compile-time via constant initialization or zero initialization. It guarantees that the variable’s initial value is evaluated and set before any dynamic initialization occurs during program startup. Unlike constexpr, constinit does not imply const. A variable declared with constinit is mutable by default and can be modified at runtime. However, constinit can be explicitly combined with const to make the variable immutable. The specifier strictly enforces the timing and nature of the initialization, not the mutability of the variable itself.

Syntax and Storage Duration Constraints

The constinit specifier can only be applied to variables possessing static or thread storage duration. It cannot be applied to variables with automatic storage duration (local variables) unless they are explicitly declared static or thread_local.
consteval int compute_initial_value() { return 42; }
int compute_dynamic_value() { return 42; }

// Valid: Global scope implies static storage duration. Initialized at compile-time.
constinit int g_val = compute_initial_value(); 

// Valid: constinit can be combined with const to enforce immutability.
const constinit int g_readonly_val = 10;

// Invalid: compute_dynamic_value() is not a constant expression.
// constinit int g_invalid = compute_dynamic_value(); 

void process() {
    // Valid: Local static variable has static storage duration.
    constinit static int s_val = 100;
    
    // Valid: Local thread_local variable has thread storage duration.
    constinit thread_local int t_local_val = 200;
    
    // Valid: constinit variables are mutable at runtime (unless declared const).
    s_val++; 
    g_val = 50;

    // Invalid: Automatic storage duration is not allowed.
    // constinit int local_val = 200; 
}

// Valid: Thread-local storage duration at namespace scope is permitted.
constinit thread_local int t_val = 5;

Technical Rules and Behavior

  1. Constant Expression Requirement: The initializer for a constinit variable must be a valid constant expression. If the compiler cannot evaluate the initializer at compile-time, the program is ill-formed.
  2. Zero and Default Initialization: If a constinit variable is declared without an explicit initializer, it undergoes default initialization. Because constinit variables must have static or thread storage duration, they are strictly zero-initialized before any other initialization takes place. For scalar types, this zero-initialization inherently satisfies the constinit requirement because it ensures the variable does not undergo dynamic initialization. However, for class types, default initialization subsequently invokes the default constructor. If the class has a non-constexpr default constructor, this results in dynamic initialization, violating the constinit constraint and causing a compilation error.
  3. Declaration vs. Definition: If the constinit specifier is present on any declaration of a variable, it must be present on the initializing declaration (the definition). Omitting constinit on the definition makes the program ill-formed. Conversely, C++ allows omitting constinit on a non-defining declaration (such as an extern declaration) as long as it is present on the definition.
  4. Exclusivity: constinit cannot be combined with constexpr on the same variable declaration. constexpr already implies constant initialization, making constinit redundant and syntactically invalid when paired.

Keyword Comparison Matrix

To understand constinit mechanically, it is best contrasted with const and constexpr regarding initialization timing and runtime mutability:
SpecifierInitialization TimeRuntime MutabilityStorage Duration Limit
constCompile-time or RuntimeImmutable (Read-only)None
constexprCompile-time strictlyImmutable (Read-only)None
constinitCompile-time strictlyMutable (unless explicitly const)Static or Thread only

Class Member Syntax

When applied to class members, constinit requires the member to be static. It can be combined with inline to allow in-class definition of the static member.
class Configuration {
public:
    // Valid: inline static allows in-class definition with constinit
    constinit inline static int max_connections = 10;
    
    // Valid non-defining declaration with constinit
    constinit static int timeout_ms;

    // Valid non-defining declaration without constinit
    static int retry_count;
};

// Out-of-line definition: MUST include constinit because it was present 
// on the declaration. Omitting it here makes the program ill-formed.
constinit int Configuration::timeout_ms = 5000;

// Out-of-line definition: MUST include constinit to enforce compile-time 
// initialization, even though it was omitted on the non-defining declaration.
constinit int Configuration::retry_count = 3;
Master C++ with Deep Grasping Methodology!Learn More