1. Problem It Solves
Small deterministic lookup tables need not be rebuilt during startup or handwritten as error-prone constants. C++14 relaxed constexpr can fill a small literal table type in a loop, and a variable template exposes one table per size.
Focus on the smallest useful form, its observable behavior, and its safety boundary.
2. Prerequisites
Days 14-15 and 37: relaxed
constexpr, variable templates, built-in arrays, loops, and non-type parameters.
3. Core Idea
A generator is a pure compile-time recipe and the variable-template specialization stores its result. The table then behaves like ordinary read-only array data at runtime.
Identify the objects and types, today's operation, and the printed result. This connects syntax to behavior.
4. Minimal Syntax
template<std::size_t N>
struct Table { int values[N]{}; };
template<std::size_t N>
constexpr Table<N> squares = make_squares<N>();5. How It Works
The non-type parameter fixes array length and the generator initializes every element in a bounded loop.
The variable-template initializer calls that generator in a constant-expression context for the requested size.
static_assertchecks one entry and runtime prints the already-generated square table.
6. Common Mistakes
A supposedly compile-time generator that depends on runtime input cannot initialize a
constexprtable.Do not copy the pattern without checking array bounds, deterministic inputs, constant-expression operations, integer overflow, compile-time cost, and table size. A program may compile while still having the wrong lifetime, ownership, invalidation, ordering, or performance behavior.
7. When to Use It
Use it when a small deterministic table removes repeated runtime work and compile-time validation is valuable.
Avoid it when the table is huge, depends on runtime configuration, or increases build cost and binary size without benefit.
8. Simple Example
A generator fills five positions in a tiny literal table with index squares. The variable template squares<5> stores the result, and output shows 0, 1, 4, 9, 16.
The .cpp file uses fixed data. Predict its output, compile it, then change one value and test the prediction.
Complete sample code
Source file
cpp14/38_compile_time_tables_constexpr_variable_templates/main.cpp
#include <cstddef>
#include <iostream>
template <std::size_t N>
struct Table {
int values[N]{};
constexpr int& operator[](std::size_t index) { return values[index]; }
constexpr const int& operator[](std::size_t index) const {
return values[index];
}
};
template <std::size_t N>
constexpr Table<N> make_squares() {
Table<N> result{};
for (std::size_t i = 0; i < N; ++i) {
result[i] = static_cast<int>(i * i);
}
return result;
}
template <std::size_t N>
constexpr Table<N> squares = make_squares<N>();
int main() {
static_assert(squares<5>[3] == 9, "compile-time table");
std::cout << "squares:";
for (std::size_t i = 0; i < 5; ++i) std::cout << ' ' << squares<5>[i];
std::cout << "\n";
}
9. Key Takeaways
Compile-time tables trade build work and binary data for predictable runtime lookup.
A generator is a pure compile-time recipe and the variable-template specialization stores its result. The table then behaves like ordinary read-only array data at runtime.
The compiler or library follows a precise rule; verify array bounds, deterministic inputs, constant-expression operations, integer overflow, compile-time cost, and table size.
Prefer the smallest form that communicates intent and measure costs when performance matters.
10. Self-Check Questions
Easy — What is the main purpose of Compile-Time Tables with constexpr and Variable Templates?
Medium — What value is stored at index 3 of
squares<5>?Hard — Why can excessive table size or expensive generation hurt builds even though runtime becomes cheaper?