if constexpr, NTTPs, and Compile-Time Data
Non-type template parameters turn sizes and array lengths into types. Combined with if constexpr, they carry whole tables into compile time.
Non-type template parameters (NTTPs)
Template parameters can be values: sizes, enumerations, pointers โ anything constant-expression-able. The most common: std::size_t:
template <std::size_t N> struct Ring {
int data_[N]{};
std::size_t head_ = 0;
void push(int v) { data_[head_++ % N] = v; }
static constexpr std::size_t capacity() { return N; }
};
Every Ring<8> and Ring<1024> is a distinct type with zero heap allocations and a compile-time-known capacity.
Array-bound NTTP โ free string lengths
Deducing N from an array reference gives you the literal's length for free: template <std::size_t N> constexpr std::size_t strLen(const char (&s)[N]) { return N - 1; } โ strLen("abcd") is 4, computed at compile time, no strlen.
Partial specialization over NTTPs
The two compose: Capacity<std::array<T, N>> peels the size out of the type โ the standard library's own tuple_size works this way.
Watch out: each distinct N instantiates a distinct type. A Ring<8> is not a Ring<9>; if you need runtime-variable capacity, that is a std::vector job, not an NTTP job.