In C++, Non-Type Template Parameters (NTTPs) are a type of template parameter that represent values (rather than types). These values are specified when a template is instantiated, and they can be used just like regular constants within the template. Non-type template parameters can represent things like integers, pointers, or references, and they are evaluated at compile time.
IntNTTPExample.cpp
#include <iostream>
template<int N>
class Array {
int arr[N]; // Array with size N
public:
Array() {
fill(0);
}
void fill(int value) {
for (int i = 0; i < N; ++i) {
arr[i] = value;
}
}
void print() const {
for (int i = 0; i < N; ++i) {
std::cout << arr[i] << " ";
}
std::cout << std::endl;
}
};
int main() {
Array<5> myArray; // Array of size 5
myArray.fill(10);
myArray.print();
constexpr int size = 3; // Must be a constant expression
Array<size> myArray2; // Array of size 3
myArray2.fill(30);
myArray2.print();
return 0;
}
10 10 10 10 10
30 30 30
Note in this example the template parameter N isn't declared as template or class as ordinary template parameters that represent types are declared. Instead it has a specific type of int in this case. When the class is instantiated, a specific compile-time integer must be supplied.
Array<> classes instantiated with different NTTP integer values are not the same type, i.e. they can not be assigned to the other.
Compile-time pointers such as pointers to functions can also be used as NTTP in templates. In the next example we create a template function that takes a NTTP parameter that points to a function.
PointerNTTPExample.cpp
#include <iostream>
void printHello() {
std::cout << "Hello" << std::endl;
}
void printGoodbye() {
std::cout << "Goodbye" << std::endl;
}
template<void (*Func)()>
class CallFunction {
public:
void call() const {
Func(); // Call the function pointed to by Func
}
};
int main() {
CallFunction<&printHello> helloCaller;
CallFunction<&printGoodbye> goodbyeCaller;
helloCaller.call();
goodbyeCaller.call();
return 0;
}
Hello
Goodbye
In the CallFunction<>() template function, the template parameter is a literal function address. This allows each instantiation of CallFunction<>() to call a different function.
In the next example we see that even an enumeration constant can be used as an NNTP.
EnumNTTPExample.cpp
#include <iostream>
enum class Color { Red, Green, Blue };
template<Color C>
class Paint {
public:
void apply() const {
if (C == Color::Red)
std::cout << "Painting in Red" << std::endl;
else if (C == Color::Green)
std::cout << "Painting in Green" << std::endl;
else
std::cout << "Painting in Blue" << std::endl;
}
};
int main() {
Paint<Color::Red> redPainter;
Paint<Color::Green> greenPainter;
redPainter.apply();
greenPainter.apply();
return 0;
}
Painting in Red
Painting in Green
