C++, End to End
Constexpr and Compile-Time Evaluation
Most computation in a C++ program happens when the program runs. Some of it can happen while the program is being *compiled* instead, which means it costs nothing at runtime and, for things like array sizes, can unlock language features that need a compile-time constant in the first place. This chapter is about the tools that make that possible.
constexpr variables
A constexpr variable's value must be computable at compile time, and the compiler enforces that: if you try to initialize one with something that isn't knowable until runtime, it's a compile error, not a runtime one.
This is stricter than plain const. A const int can be initialized from a runtime value (const int x = some_function(); compiles fine, x just can't be reassigned afterward). constexpr additionally demands the value be known at compile time.
constexpr functions
A function marked constexpr *can* run at compile time, if all of its arguments are compile-time constants and its body is simple enough (no undefined behavior, since the compiler has to actually evaluate it). Called with runtime values instead, the exact same function just runs normally at runtime. It's one function definition serving both cases.
"Can run at compile time" is not the same as "always runs at compile time". Whether a particular call actually gets evaluated at compile time depends on context (is the result being used somewhere that demands a compile-time constant, like an array bound or another constexpr variable's initializer) and, in some cases, is left up to the compiler to decide as an optimization.
consteval: forcing compile-time evaluation
C++20 added consteval, which is stricter still: a consteval function (an "immediate function") must always be evaluated at compile time. Calling it with anything that isn't a compile-time constant is a compile error, not a fallback to runtime execution.
Use consteval when a function genuinely only makes sense at compile time (generating a lookup table baked into the binary, validating a format string) and you want the compiler to guarantee that rather than silently falling back to a runtime call.
if constexpr: compile-time branching
Ordinary if picks a branch at runtime, but both branches still have to compile even if one is never taken for a given type. if constexpr (C++17) discards the untaken branch entirely at compile time, which matters most inside templates, where the discarded branch might not even be valid code for the type actually being used.
static_assert: compile-time checks
Covered in more depth in the error-handling chapter, but worth repeating here since it's the natural companion to everything above: static_assert(condition, "message") fails the *build* if condition isn't true, checked entirely at compile time. It's the standard way to validate assumptions about constexpr computations, type sizes, or template parameters before the program ever runs.
Try it yourself: make a runtime function compile-time capable›
This function works fine, but it can only ever run at runtime. Add constexpr to its signature, then declare a constexpr variable that calls it, forcing compile-time evaluation, and confirm it still works correctly when called with a genuinely runtime value too.