Skip to main content

constexpr, consteval, constinit

advanced11 min readLesson 148 of 204

Three keywords, three contracts: can-run-at-compile-time, must-run-at-compile-time, and must-be-initialized-statically.

constexpr โ€” "can be constant"

On variables: the initializer must be a constant expression. On functions: the function may run at compile time when given constant arguments โ€” and still runs at runtime otherwise. One implementation, both worlds:

constexpr int fib(int n) { return n < 2 ? n : fib(n - 1) + fib(n - 2); }
constexpr int F = fib(10);          // compile time
int x = fib(runtimeInput);          // runtime, same body

consteval โ€” "must be immediate"

An immediate function: every call produces a compile-time constant; a runtime call is a hard error. Use for constructors of "compile-time-only" types and for functions whose result must be baked in:

consteval int square(int n) { return n * n; }
constexpr int S = square(9);        // OK
// int y = square(runtimeVal);      // error: not a constant expression

constinit โ€” "initialized statically, checked at compile time"

constinit guarantees static initialization (no runtime constructor, no SIOF โ€” static initialization order fiasco) while keeping the variable mutable:

constinit std::atomic<unsigned> requests{0};   // zero-init at load, mutable later

The rule of thumb: constexpr = value is fixed; constinit = initialization is fixed, value is not.

Now practice

Compile-Time ComputingA constexpr parser baked into constants, consteval validation gates, and constexpr selectors.3 challenges ยท ยท ~22 min