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