Skip to Content
Course content

21: Constants: const and constexpr

Click on the "Edit" button in the top corner of the screen to edit your slide content.

I remember working on a small 2D platformer early in my career. I had a variable for GRAVITY set to -9.8. A few weeks into development, I spent nearly an entire afternoon debugging why the player suddenly started floating upward whenever they touched a specific wall. It turned out I had accidentally used the GRAVITY variable as a temporary accumulator in a collision loop. Because it was just a regular double, the compiler didn't see a problem with me overwriting the global gravity value with a positive number. If I had simply marked that variable as a constant, the compiler would have screamed at me the moment I typed that line, saving me four hours of frustration.

Stopping the Accidental Mutation

In C++, the const keyword is your way of telling the compiler—and your future self—that a value should never change once it's initialized. It's a contract. When you mark a variable as const, any attempt to modify it will result in a compile-time error. This is far better than finding a bug at runtime when your program crashes or behaves erratically because a value changed when it shouldn't have.

Now, const is flexible. You can initialize a const variable with a value that isn't known until the program is actually running. For example, you might get a value from a user's input or a configuration file and then lock it down for the rest of the program's execution:

double user_scale = GetUserPreference(); 
const double locked_scale = user_scale; // This is fine.
// locked_scale = 2.0; // This would trigger a compiler error.

I often suggest using const by default for everything. If you don't explicitly need to change a variable, don't let it be mutable. It makes your code easier to reason about because you don't have to track the "state" of a variable across a hundred lines of code; you know it's the same value it was at the start.

Moving the Work to the Compiler

Then we have constexpr. While const says "this won't change," constexpr says "this is known at compile-time." This is a critical distinction. When you use constexpr, you are telling the compiler to perform the calculation while it's building your app, rather than making the CPU do it every time the program runs.

Think of it as a performance optimization. If you have a complex mathematical formula that relies on fixed values, why calculate it a million times a second in a game loop when you can calculate it once during compilation?

constexpr int MAX_BUFFER_SIZE = 1024 * 64;
constexpr double SECONDS_IN_HOUR = 60 * 60;

// You can even have constexpr functions!
constexpr int square(int x) {
    return x * x;
}

int main() {
    // This value is computed at compile time, not runtime.
    constexpr int result = square(10); 
}

A quick rule of thumb: if the value is a hard-coded literal or a calculation based on other hard-coded literals, use constexpr. If the value depends on something that happens while the program is running (like a function call to the OS or user input), use const. All constexpr variables are implicitly const, but not all const variables can be constexpr.




📋 Practical Task

Exercise: Building a Fixed-Rate Physics Configurator

You are tasked with creating a configuration header for a physics engine. The engine requires certain values to be absolutely immutable and computed at compile-time for performance, while other values are set once at startup based on the hardware's capabilities.

Requirements:

  • Create a constexpr value for PI (3.14159) and a constexpr value for GRAVITY_EARTH (-9.81).
  • Create a constexpr function called calculate_force that takes mass and acceleration and returns the product.
  • In your main function, simulate a "hardware check" by creating a variable detected_cpu_cores.
  • Use that detected_cpu_cores variable to initialize a const variable called MAX_PHYSICS_THREADS.
  • Attempt to change MAX_PHYSICS_THREADS or PI and observe the compiler error (then comment out the failing line so the code compiles).
  • Print the result of calculate_force using a constexpr variable to ensure the calculation is handled by the compiler.
Rating
0 0

There are no comments for now.

to be the first to leave a comment.