Non-Type Template Parameters
Parameterize templates with compile-time constant values like integers.
What Are Non-Type Template Parameters?
A non-type template parameter is a template parameter that represents a compile-time constant value (like an integer) rather than a type. This allows you to parameterize templates on values such as sizes, counts, or other constants known at compile time.
So far, template parameters have represented types. But templates can also accept values. A non-type template parameter (NTTP) is a placeholder for a compile-time constant value rather than a type.
You've already used one if you've worked with std::array:
std::array<double, 100> measurements{}; // 100 is a non-type template parameter
The 100 determines the array's size at compile time.
Declaring non-type template parameters
Declare an NTTP by specifying its type in the template parameter list:
template <int Rows, int Cols>
void createGrid()
{
// Rows and Cols are compile-time constants here
}
Example usage:
#include <iostream>
template <int Repetitions>
void printStars()
{
for (int i{}; i < Repetitions; ++i)
std::cout << '*';
std::cout << '\n';
}
int main()
{
printStars<5>(); // *****
printStars<10>(); // **********
return 0;
}
The compiler generates separate functions for each value:
void printStars_5() // Generated from printStars<5>
{
for (int i{}; i < 5; ++i)
std::cout << '*';
std::cout << '\n';
}
void printStars_10() // Generated from printStars<10>
{
for (int i{}; i < 10; ++i)
std::cout << '*';
std::cout << '\n';
}
Allowed types for NTTPs
Non-type template parameters can be:
| Type | Example |
|---|---|
| Integral types | int, std::size_t, char, bool |
| Enumeration types | enum class Color { Red, Green, Blue } |
std::nullptr_t |
nullptr |
| Floating-point (C++20) | double, float |
| Pointers/references | int*, const char* |
| Literal class types (C++20) | Custom constexpr classes |
Compile-time requirements
NTTP arguments must be known at compile time:
template <int Size>
void allocateBuffer()
{
char buffer[Size]; // Size must be a compile-time constant
}
int main()
{
allocateBuffer<256>(); // OK: literal is compile-time constant
constexpr int size{ 512 };
allocateBuffer<size>(); // OK: constexpr variable
int userSize{};
std::cin >> userSize;
allocateBuffer<userSize>(); // Error: runtime value
return 0;
}
Combining type and non-type parameters
Templates often mix both kinds:
#include <iostream>
template <typename T, int Count>
void repeat(T value)
{
for (int i{}; i < Count; ++i)
std::cout << value << ' ';
std::cout << '\n';
}
int main()
{
repeat<int, 3>(42); // 42 42 42
repeat<char, 5>('X'); // X X X X X
repeat<double, 2>(3.14); // 3.14 3.14
return 0;
}
The type parameter T determines what kind of value to print. The non-type parameter Count determines how many times.
Practical application: fixed-size containers
NTTPs are ideal for compile-time sizing:
template <typename T, int Capacity>
class RingBuffer
{
private:
T data[Capacity]{};
int head{};
int tail{};
int count{};
public:
void push(T value)
{
data[tail] = value;
tail = (tail + 1) % Capacity;
if (count < Capacity)
++count;
else
head = (head + 1) % Capacity;
}
T front() const { return data[head]; }
int size() const { return count; }
constexpr int capacity() const { return Capacity; }
};
Usage:
RingBuffer<int, 4> buffer{};
buffer.push(10);
buffer.push(20);
buffer.push(30);
// buffer.capacity() returns 4 at compile time
Compile-time validation with static_assert
NTTPs enable compile-time error checking:
#include <cmath>
template <int Divisor>
int safeDivide(int numerator)
{
static_assert(Divisor != 0, "Divisor cannot be zero");
return numerator / Divisor;
}
int main()
{
safeDivide<5>(100); // OK: returns 20
safeDivide<0>(100); // Compile error: static_assert fails
return 0;
}
The error is caught during compilation, not at runtime.
Type deduction for NTTPs (C++17)
Use auto to let the compiler deduce the NTTP type:
#include <iostream>
template <auto Value>
void display()
{
std::cout << Value << '\n';
}
int main()
{
display<42>(); // Deduces int
display<'A'>(); // Deduces char
display<true>(); // Deduces bool
display<3.14>(); // Deduces double (C++20)
return 0;
}
Naming conventions
Use descriptive names when the purpose is clear:
template <int Rows, int Cols> // Clear meaning
template <int BufferSize> // Clear meaning
template <int MaxRetries> // Clear meaning
For generic cases, N is conventional:
template <int N> // Generic integer parameter
template <typename T, int N> // Common pattern
Summary
Non-type template parameters accept compile-time constant values:
| Aspect | Details |
|---|---|
| Declaration | template <int N>, template <typename T, int N> |
| Argument requirements | Must be compile-time constants |
| Common types | int, std::size_t, bool, enums |
| C++17 feature | auto for type deduction |
| C++20 additions | Floating-point types, literal class types |
Primary uses:
- Fixed-size arrays and containers
- Compile-time configuration
- Static assertions for validation
- Constant-based algorithm variations
Create an account to track your progress and access interactive exercises. Already have one? Sign in.
Non-Type Template Parameters - Quiz
Test your understanding of the lesson.
Practice Exercises
Non-Type Template Parameters
Create templates with compile-time constant values as parameters.
Lesson Discussion
Share your thoughts and questions
No comments yet. Be the first to share your thoughts!