C++
Completed
-
Section 1: Getting Started
-
Section 2: Core Syntax
-
Section 3: Object-Oriented C++
-
Section 4: Memory Management
-
Section 5: Templates and Generic Programming
-
Section 6: The Standard Template Library
-
Section 7: Modern C++ Features
-
Section 8: Error Handling
-
Section 9: Multithreading
-
Section 10: Operator Overloading and Type Conversion
-
Section 11: Advanced Topics
-
Section 12: Tooling and Build Systems
-
Section 13: Design Patterns in C++
-
Section 14: Interfacing with C and Systems Programming
-
Section 15: Networking and IPC Basics
-
Section 16: Graphics and Game Programming Basics
-
Section 17: The Boost Libraries
-
Section 18: Data Structures and Algorithms in C++
-
Section 19: Practical Projects
-
Section 20: More Concurrency Patterns
-
Section 21: More OOP and Design Practice
-
Section 22: File I/O and Streams
-
Section 23: More Standard Library
-
Section 24: Practice Exercises
-
Section 25: Interview and Algorithm Practice
-
Section 26: Compiler and Language Internals
-
Section 27: GUI and Application Frameworks Overview
-
Section 28: Testing and Quality Practices
-
Section 29: Numerics Library
-
Section 30: Concepts Library (C++20)
-
Section 31: Ranges Library (C++20) In Depth
-
Section 32: More Utility Library
21: Constants: const and constexpr
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
constexprvalue forPI(3.14159) and aconstexprvalue forGRAVITY_EARTH(-9.81). - Create a
constexprfunction calledcalculate_forcethat takes mass and acceleration and returns the product. - In your
mainfunction, simulate a "hardware check" by creating a variabledetected_cpu_cores. - Use that
detected_cpu_coresvariable to initialize aconstvariable calledMAX_PHYSICS_THREADS. - Attempt to change
MAX_PHYSICS_THREADSorPIand observe the compiler error (then comment out the failing line so the code compiles). - Print the result of
calculate_forceusing aconstexprvariable to ensure the calculation is handled by the compiler.
There are no comments for now.