constinit specifier (since C++20)

From cppreference.com
< cpp‎ | language
 
 
C++ language
General topics
Flow control
Conditional execution statements
if
Iteration statements (loops)
for
range-for (C++11)
Jump statements
Functions
Function declaration
Lambda function expression
inline specifier
Dynamic exception specifications (until C++17*)
noexcept specifier (C++11)
Exceptions
Namespaces
Types
Specifiers
const/volatile
decltype (C++11)
auto (C++11)
constexpr (C++11)
consteval (C++20)
constinit (C++20)
Storage duration specifiers
Initialization
Expressions
Alternative representations
Literals
Boolean - Integer - Floating-point
Character - String - nullptr (C++11)
User-defined (C++11)
Utilities
Attributes (C++11)
Types
typedef declaration
Type alias declaration (C++11)
Casts
Memory allocation
Classes
Class-specific function properties
explicit (C++11)
static

Special member functions
Templates
Miscellaneous
 
 

Explanation

The constinit specifier declares a variable with static or thread storage duration. If a variable is declared with constinit, its initializing declaration must be applied with constinit. If a variable declared with constinit has dynamic initialization (even if it is performed as static initialization), the program is ill-formed. If no constinit declaration is reachable at the point of the initializing declaration, the program is ill-formed, no diagnostic required.

constinit cannot be used together with constexpr. When the declared variable is a reference, constinit is equivalent to constexpr. When the declared variable is an object, constexpr mandates that the object must have static initialization and constant destruction and makes the object const-qualified, however, constinit does not mandate constant destruction and const-qualification. As a result, an object of a type which has constexpr constructors and no constexpr destructor (e.g. std::shared_ptr<T>) might be declared with constinit but not constexpr.

const char* g() { return "dynamic initialization"; }
constexpr const char* f(bool p) { return p ? "constant initializer" : g(); }
 
constinit const char* c = f(true);     // OK
// constinit const char* d = f(false); // error

constinit can also be used in a non-initializing declaration to tell the compiler that a thread_local variable is already initialized, reducing overhead that would otherwise be incurred by a hidden guard variable.

extern thread_local constinit int x;
int f() { return x; } // no check of a guard variable needed

Notes

Feature-test macro Value Std Feature
__cpp_constinit 201907L (C++20) constinit

Keywords

constinit

Example

#include <cassert>
 
constexpr int square(int i)
{
    return i * i;
}
 
int twice(int i)
{
    return i + i;
}
 
constinit int sq = square(2); // OK: init is done at compile time
// constinit int x_x = twice(2); // Error: compile time initializer required
 
int square_4_gen()
{
    static constinit int pow = square(4);
 
    // constinit int prev = pow; // Error: 'constinit' can only be applied to a
                                 // variable with static or thread storage duration
    int prev = pow;
    pow = pow * pow;
    return prev;
}
 
int main()
{
    assert(sq == 4);
    sq = twice(1); // Unlike constexpr this value can be changed later at runtime
    assert(sq == 2);
 
    assert(square_4_gen() == 16);
    assert(square_4_gen() == 256);
    assert(square_4_gen() == 65536);
}

Defect reports

The following behavior-changing defect reports were applied retroactively to previously published C++ standards.

DR Applied to Behavior as published Correct behavior
CWG 2543 C++20 the behavior was unclear if the variable declared with constinit
is dynamically initialized as part of static initialization
the program is ill-
formed in this case

See also

consteval specifier(C++20) specifies that a function is an immediate function, that is, every call to the function must be in a constant evaluation
constexpr specifier(C++11) specifies that the value of a variable or function can be computed at compile time
constant expression defines an expression that can be evaluated at compile time
constant initialization sets the initial values of the static variables to a compile-time constant
zero initialization sets the initial value of an object to zero