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
93: constexpr Functions and Compile-Time Computation
I was working on a project recently where I needed a static lookup table for some physics calculations. I knew the size of the table had to be based on a specific mathematical formula—essentially a factorial of a small constant—and since it was a fixed-size array, I wanted it to be decided at compile-time. No heap allocations, no runtime overhead.
The Array Size Headache
My first instinct was to just write a quick helper function. I figured the compiler would be smart enough to see it was a simple calculation and just "plug it in." Here is what I tried:
int get_table_size(int n) {
if (n <= 1) return 1;
return n * get_table_size(n - 1);
}
int main() {
// I want a buffer based on 5! (120)
int buffer[get_table_size(5)];
return 0;
}
I hit compile and, predictably, the compiler yelled at me. It told me that the array bound must be a constant expression. Even though get_table_size(5) is obviously 120 to any human, the compiler sees a function call. To the compiler, a function call is a "runtime event"—something that happens while the program is actually running, not while it's being built. It can't guarantee that get_table_size won't do something unpredictable, so it refuses to use it to size a stack array.
Teaching the Compiler to Do the Math
This is where constexpr comes in. By adding this keyword, I'm essentially telling the compiler: "If the inputs to this function are known at compile-time, you are allowed—and encouraged—to evaluate this entire function right now, during the build process."
Let's tweak the code:
constexpr int get_table_size(int n) {
if (n <= 1) return 1;
return n * get_table_size(n - 1);
}
int main() {
// Now this works perfectly.
int buffer[get_table_size(5)];
return 0;
}
Now it compiles. The compiler evaluates get_table_size(5), replaces it with the literal 120, and the binary that actually ships to the user contains no call to that function for the array sizing. It's essentially a zero-cost abstraction.
The Dual-Purpose Nature of constexpr
One thing that always tripped me up when I first learned this is whether a constexpr function must be evaluated at compile-time. I wondered: "If I mark it constexpr, can I still use it with a variable that the user types in at runtime?"
I decided to test it out:
constexpr int get_table_size(int n) {
if (n <= 1) return 1;
return n * get_table_size(n - 1);
}
int main() {
int user_val;
std::cin >> user_val;
// Can I use a constexpr function here?
int result = get_table_size(user_val);
std::cout << "Result: " << result;
}
It works. This is the "magic" of constexpr. It doesn't force compile-time evaluation; it enables it. If the arguments are constants, it happens at compile-time. If the arguments are runtime variables, it behaves like a totally normal function. You get one piece of code that handles both scenarios optimally.
Hitting the Wall
Of course, you can't put just anything in a constexpr function. I tried to add a print statement inside my factorial function just to see the recursion happening during compilation:
constexpr int get_table_size(int n) {
std::cout << "Calculating for " << n << "\n"; // Oops
if (n <= 1) return 1;
return n * get_table_size(n - 1);
}
The compiler immediately complained. Why? Because std::cout is a runtime object. You cannot perform I/O operations during the compilation process. The compiler is essentially a program that reads your code and writes a binary; it isn't "running" your program in a virtual machine. It can only perform calculations and logic that can be resolved purely through the language's type system and constant values.
Basically, if the operation requires the operating system (like printing to a console, reading a file, or allocating memory via malloc), it cannot be constexpr.
📋 Practical Task
Exercise: Pre-calculating a Coordinate Grid Capacity
You are building a 3D game engine. To optimize memory, you need to pre-calculate the total number of cells in a nested coordinate grid based on three dimensions (X, Y, and Z), but these dimensions are constants defined by the game's level settings.
Your Task:
- Create a
constexprfunction namedcalculate_grid_cellsthat takes three integers (width, height, depth) and returns their product. - In your
mainfunction, define threeconstexpr intvariables forlvl_width,lvl_height, andlvl_depth. - Use the
calculate_grid_cellsfunction to initialize astd::arrayof integers. The size of the array must be determined by yourconstexprfunction. - Print the size of the array to the console to verify the calculation was performed.
Goal: Ensure the array is sized at compile-time, meaning the function call must be valid as a template argument for std::array.
There are no comments for now.