What are Templates in C++
beginner
c++11
Templates let us define functions or classes with placeholders for the types of their parameters or members respectively. The compiler then instantiates the function or class for us at compile time based on how the function or class is used elsewhere in the program. Instantiation means to turn a template into a concrete function or class. Because instantiation happens at compile time the full definition of the function or class must be visible at the time of usage, typically in your .cpp files. It’s often the case that templates are fully defined in header files so that they may be visible to the compiler everywhere in your program.
Template Functions
Here’s how to write a template function that cubes anything that can be multiplied by itself.
#include <iostream> template <typename T> T cube(T x) { return x * x * x; } struct Point { int x, y, z; friend Point operator *(const Point& a, const Point& b) { return Point{a.x * b.x, a.y * b.y, a.z * b.z}; } }; int main() { int x = 7; std::cout << cube(x) << "\n"; float pi = 3.14159f; std::cout << cube(pi) << "\n"; Point p{1, 2, 3}; Point p3 = cube(p); std::cout << p3.x << ", " << p3.y << ", " << p3.z << "\n"; }
343 31.0062 1, 8, 27
Inferred Template Types
In the above example the template type T
of the cube
function template was inferred for us by the compiler.
This is because the compiler observes the type of the parameters x
, pi
, and p
to be int
, float
, and Point
respectively at the time of instantiation.
In cases where it’s not clear or you wish to be explicit you can use angle brackets (<
and >
) to specify the type manually.
#include <iostream> template <typename T> T cube(T x) { return x * x * x; } int main() { int x = 7; std::cout << cube<int>(x) << "\n"; float pi = 3.14159f; std::cout << cube<float>(pi) << "\n"; }
343 31.0062
Template Classes
In the first example we used a Point
struct to hold three integer values.
We can template the Point
struct as well to support other types.
We can combine our Point
struct template with our cube
template function to cube Points of any type.
#include <iostream> template <typename T> T cube(T x) { return x * x * x; } template <typename T> struct Point { T x, y, z; friend Point operator *(const Point& a, const Point& b) { return Point{a.x * b.x, a.y * b.y, a.z * b.z}; } }; int main() { Point<int> p{1, 2, 3}; Point p3 = cube(p); std::cout << p3.x << ", " << p3.y << ", " << p3.z << "\n"; Point<float> pf{1.1, 2.2, 3.3}; Point pf3 = cube(pf); std::cout << pf3.x << ", " << pf3.y << ", " << pf3.z << "\n"; }
1, 8, 27 1.331, 10.648, 35.937
Non-Type Template Parameters
We can also use templates to specify integer values of structs at compile time.
std::array<T, N>
uses a non-type template parameter N
to declare the number of elements it holds.
We can implement our own simplified Array
to see how this works.
#include <iostream> template <typename T> T cube(T x) { return x * x * x; } template <typename T, size_t N> class Array { public: T data_[N]; T& operator[](size_t i) { return data_[i]; } T* begin() { return data_; } T* end() { return data_ + N; } }; int main() { Array<int, 3> a{1, 2, 3}; for (auto& it : a) { it = cube(it); } std::cout << a[0] << ", " << a[1] << ", " << a[2] << "\n"; }
1, 8, 27