Non-Type Template Parameters in C++20
This article explores Non-Type Template Parameters (NTTPs) in C++, explaining what they are, their historical limitations, and the major enhancements introduced in C++20. Readers will gain a clear understanding of how C++20 expands NTTP capabilities to support floating-point values, user-defined class types (structural types), and compile-time string literals, enabling cleaner and more expressive compile-time metaprogramming.
What Are Non-Type Template Parameters?
In C++, templates typically accept types (such as int,
std::string, or custom classes) using the
typename or class keywords. A Non-Type
Template Parameter (NTTP), by contrast, is a template parameter that
represents a compile-time constant value rather than a type.
Prior to C++20, the types permitted as NTTPs were strictly limited. Permissible NTTP types included:
- Integral types (e.g.,
int,char,size_t) - Enumeration types
- Pointers and references to objects or functions with linkage
std::nullptr_t
The most common example of a traditional NTTP is the second parameter
of std::array:
template <typename T, std::size_t N>
class array;
std::array<int, 10> numbers; // 10 is an NTTPHistorically, attempts to pass floating-point numbers, objects with complex state, or string literals directly as template arguments resulted in compilation errors.
How C++20 Expanded NTTPs
C++20 significantly relaxed these restrictions by introducing support for floating-point types, structural class types, and custom compile-time strings.
1. Floating-Point Numbers as NTTPs
C++20 permits floating-point types (float,
double, long double) to serve directly as
template parameters. This eliminates the need for workarounds such as
passing ratios or scaled integers for compile-time scientific
calculations.
template <double Threshold>
struct SensorFilter {
bool isAbove(double value) {
return value > Threshold;
}
};
SensorFilter<0.75> filter;2. Structural Class Types
The most profound addition to NTTPs in C++20 is the concept of structural types. An object of a class type can be used as a template argument if it qualifies as a structural type.
A type is a structural type if it is:
- A scalar type, or
- An lvalue reference type, or
- A literal class type where:
- All base classes and non-static data members are public and non-mutable, and
- The types of all bases and non-static data members are themselves structural types.
struct Point {
int x;
int y;
};
template <Point P>
void render() {
// P.x and P.y are accessible at compile time
}
int main() {
render<Point{10, 20}>(); // Valid in C++20
}Two template instantiations are considered identical if the bitwise or value-based equivalence of their structural arguments matches.
3. Compile-Time String Literals
A longstanding limitation of C++ templates was the inability to pass string literals directly into templates. Because C++20 allows structural class types as NTTPs, developers can define a literal wrapper class to accept and store string literals at compile time.
#include <algorithm>
#include <cstddef>
template <std::size_t N>
struct FixedString {
char data[N];
constexpr FixedString(const char (&str)[N]) {
std::copy_n(str, N, data);
}
};
template <FixedString S>
void printTag() {
// S.data contains the string at compile time
}
int main() {
printTag<"SystemLogger">(); // Valid in C++20
}4. Class Template Argument Deduction (CTAD) for NTTPs
C++20 also introduced the auto placeholder for NTTPs,
allowing the compiler to deduce the argument's type directly at the call
site.
template <auto Value>
void inspect() {
// Type and value are deduced at compile time
}
inspect<42>(); // Value is int
inspect<'c'>(); // Value is char
inspect<3.14>(); // Value is double (C++20)Summary of Impact
C++20 transforms NTTPs from simple integer and pointer placeholders into a versatile system that accepts rich value-based data structures. By supporting floating-point types, structural classes, and compile-time strings, C++20 removes decades-old metaprogramming workarounds, resulting in safer, more expressive, and readable compile-time APIs.