Lesson 22 • Advanced
Constexpr & Compile-Time Programming
Move computation to compile time with constexpr functions, if constexpr, constexpr classes, and compile-time lookup tables.
What You'll Learn
- ✓ constexpr functions and variables
- ✓ if constexpr for compile-time branching
- ✓ constexpr classes and methods
- ✓ Compile-time lookup table generation
Why Compile-Time Computation?
If a computation always produces the same result, why compute it every time the program runs? constexpr tells the compiler: "evaluate this at compile time if all inputs are known." The result is baked into the binary — zero runtime cost.
Think of it like pre-computing a multiplication table on paper vs. calculating each product by hand during a test. The answers are already there when you need them.
constexpr Functions & Variables
Compute factorials, areas, and array sums at compile time
#include <iostream>
#include <array>
using namespace std;
// constexpr function — evaluated at compile time if possible
constexpr int factorial(int n) {
if (n <= 1) return 1;
return n * factorial(n - 1);
}
constexpr double circleArea(double radius) {
return 3.14159265358979 * radius * radius;
}
// constexpr variables — must be computable at compile time
constexpr int MAX_SIZE = 100;
constexpr double PI = 3.14159265358979;
constexpr int FACT_10 = factorial(10);
// constexpr with a
...if constexpr & constexpr Classes
Branch at compile time and build fully constexpr vector math
#include <iostream>
#include <type_traits>
#include <string>
#include <vector>
using namespace std;
// if constexpr — branches evaluated at compile time
template <typename T>
string describe(const T& value) {
if constexpr (is_integral_v<T>) {
return "Integer: " + to_string(value) +
(value % 2 == 0 ? " (even)" : " (odd)");
} else if constexpr (is_floating_point_v<T>) {
return "Float: " + to_string(value);
} else if constexpr (is_same_v<T, string>) {
...Compile-Time Lookup Tables
Generate prime sieves and square tables entirely at compile time
#include <iostream>
#include <array>
using namespace std;
// Compile-time lookup table generation
template <size_t N>
constexpr array<int, N> generateSquares() {
array<int, N> result{};
for (size_t i = 0; i < N; i++) {
result[i] = i * i;
}
return result;
}
template <size_t N>
constexpr array<bool, N> generatePrimeSieve() {
array<bool, N> sieve{};
for (size_t i = 2; i < N; i++) sieve[i] = true;
for (size_t i = 2; i * i < N; i++) {
if (sieve[i]) {
...Common Mistakes
⚠️ constexpr ≠ always compile-time: A constexpr function is evaluated at compile time only if all arguments are constexpr. Otherwise it runs at runtime.
⚠️ Forgetting static_assert: Without static_assert, you can't verify that a value was actually computed at compile time.
⚠️ constexpr vs const: const means "can't be modified." constexpr means "can be evaluated at compile time." They're different.
Pro Tips
💡 consteval (C++20): Use consteval to force compile-time evaluation — the function becomes an error if called at runtime.
💡 constinit (C++20): Guarantees a variable is initialised at compile time, preventing the static init order fiasco.
💡 LUT all the things: Trigonometric tables, CRC tables, and hash seeds are perfect candidates for compile-time generation.
📋 Quick Reference
| Keyword | Meaning | Since |
|---|---|---|
constexpr | May evaluate at compile time | C++11 |
if constexpr | Compile-time branch | C++17 |
consteval | Must evaluate at compile time | C++20 |
constinit | Must init at compile time | C++20 |
static_assert | Compile-time assertion | C++11 |
Lesson Complete!
You can now shift computation from runtime to compile time using constexpr. Next: Modern C++17 & C++20 Features — structured bindings, concepts, ranges, and more.
Sign up for free to track which lessons you've completed and get learning reminders.