C++Performance

C++ Compile-Time Programming: constexpr, consteval, constinit & Template Metaprogramming (C++23)

TT
TopicTrick Team
C++ Compile-Time Programming: constexpr, consteval, constinit & Template Metaprogramming (C++23)

C++ Compile-Time Programming: constexpr, consteval, constinit & Template Metaprogramming (C++23)


Table of Contents


The Four Compile-Time Keywords

mermaid

constexpr Functions: Rules and Restrictions

A constexpr function can execute at compile time if called with constant expressions. If called with runtime values, it runs normally as a runtime function:

cpp

Rules for constexpr functions:

  • No goto, thread-local storage, or I/O (printf, fopen, etc.)
  • No undefined behavior — the compiler catches it
  • Can call only constexpr functions
  • Local variables of non-literal types require C++14+

constexpr Classes and Objects (C++14/20)

cpp

consteval: Mandatory Compile-Time Functions (C++20)

consteval ("immediate function") forces execution at compile time. If the compiler cannot evaluate the call at compile time (because arguments aren't constant expressions), it's a hard compile error:

cpp

constinit: Initialization Order Guarantee (C++20)

The "Static Initialization Order Fiasco" occurs when one global's initialization depends on another global that may not be initialized yet:

cpp

Compile-Time Lookup Tables with IIFE

Generate lookup tables entirely at compile time — binary contains pre-computed data:

cpp

Type Traits and std::integral_constant

Type traits let you query properties of types at compile time:

cpp

static_assert: Compile-Time Validation

cpp

Frequently Asked Questions

Does constexpr always run at compile time? No — constexpr means "allowed at compile time if called with constant expressions." If called with runtime values, it executes at runtime normally. Use consteval when you want to guarantee compile-time execution and make runtime calls a hard error. Use if (std::is_constant_evaluated()) to write functions that behave differently in each context.

Why would constexpr slow down my build? Compile-time computation uses the compiler as an interpreter. Complex constexpr computations — sorting large arrays, generating CRC tables — move computation to build time. This increases build time but produces a faster binary. For very complex constexpr (400+ line recursive templates), builds can become noticeably slower. Profile build time with -ftime-report (GCC) or clang -ftime-trace.

What is the "static initialization order fiasco" and how does constinit help? In C++, the initialization order of global variables across translation units (.cpp files) is undefined. If global_b (in file2.cpp) depends on global_a (in file1.cpp), global_a might not be initialized yet when global_b's initializer runs. constinit prevents this by requiring the initializer to be a constant expression (evaluated at compile time) — no runtime initialization = no initialization order dependency.


Key Takeaway

Compile-time programming in C++ is not just a performance trick — it's a correctness mechanism. Compile-time validated format strings catch bugs before the program ships. Compile-time lookup tables eliminate an entire category of runtime computation. static_assert enforces struct layout contracts across platforms. consteval makes misuse a compile error rather than a runtime crash. Every time you push work left — from runtime to compile time — you get a faster binary and a safer system.

Read next: Type Traits & static_assert: Defensive Programming →


Part of the C++ Mastery Course — 30 modules from modern C++ basics to expert systems engineering.