Const, Constexpr, and Compile-Time Constants
Variables and Data Types

1.4 Const, Constexpr, and Compile-Time Constants

Constants prevent code from accidentally modifying variables that should remain static (like mathematical limits or configuration settings). Modern C++ has a powerful system for constants that can optimize your program by running code before it even reaches the user.

Let's look at how to declare constants and how to use modern compile time evaluation.

Const: Read-Only at Runtime

When we mark a variable as const, we tell the compiler that this variable cannot be changed after its initialization. Any attempt to modify it will throw a compile error.

A const variable is a runtime constant. This means its value can be calculated while the program is running, but once set, it remains read-only:

int x{};
std::cin >> x;
const int runtimeConstant{x}; // Valid! Value is set at runtime, then becomes read-only.
Const variables initialized at runtime.

Constexpr: Guaranteed Compile-Time Constants

Introduced in C++11, constexpr (short for constant expression) tells the compiler that the value must be known at compile time.

If you try to initialize a constexpr variable with a runtime value (like user input), the compiler will throw an error. Because the value is known at compile time, the compiler can perform optimizations that runtime constants cannot benefit from:

int x{};
std::cin >> x;
// constexpr int bad{x}; // COMPILER ERROR: Cannot evaluate runtime variable at compile time.
constexpr int good{100}; // Valid! Known at compile time.
Constexpr requires compile-time evaluation.

The Power of Compile-Time Code Execution

In modern C++, you can even declare functions as constexpr. If a function is marked as constexpr and you pass it compile-time constants, the compiler will execute the function and pre-calculate the result while building your executable. The math is calculated on your development computer, so the user's computer does zero math at runtime!

constexpr int square(int x) {
    return x * x;
}

int main() {
    constexpr int precalculated{square(5)}; // Computed at compile time!
    std::cout << precalculated;
    return 0;
}
Pre-calculating values using constexpr functions.

Under the Hood: Assembly Verification

Let's look at the generated assembly code for the main function above. Notice that there is no multiplication instruction (imul or similar) in the assembly. The compiler calculated 5 * 5 = 25 during build time and loaded the literal value 25 directly into the return register:

main:
    push    rbp
    mov     rbp, rsp
    mov     eax, 25          ; Pre-computed by the compiler! No math is done at runtime.
    pop     rbp
    ret
Assembly output showing pre-computed result.

By using constexpr, you shift execution overhead from runtime to compile-time, creating zero-latency computations in high performance applications.

Finished reading this lesson?