C++ Templates
A template is a recipe for code that works with any type. Instead of writing maxInt, maxDouble and maxString, you write one template and the compiler makes a version for every type you use it with. The whole standard library is built on templates: std::vector<int> and std::vector<std::string> come from one vector template.
Function templates
#include <iostream>
#include <string>
#include <vector>
template <typename T>
T largest(const std::vector<T>& v) {
T best = v[0];
for (const T& x : v) {
if (best < x) best = x;
}
return best;
}
int main() {
std::vector<int> nums = {42, 7, 19, 88, 3};
std::vector<std::string> words = {"pear", "apple", "mango"};
std::cout << largest(nums) << "\n";
std::cout << largest(words) << "\n";
return 0;
}88 pear
template <typename T> says that T stands for a type filled in later. The compiler deduces T from the argument: int for nums, std::string for words. The only thing largest needs from T is that two values can be compared with <.
Class templates
#include <iostream>
#include <stdexcept>
#include <string>
#include <vector>
template <typename T>
class Stack {
std::vector<T> items;
public:
void push(const T& x) { items.push_back(x); }
T pop() {
if (items.empty()) throw std::out_of_range("pop from an empty stack");
T x = items.back();
items.pop_back();
return x;
}
bool empty() const { return items.empty(); }
std::size_t size() const { return items.size(); }
};
int main() {
Stack<int> numbers;
numbers.push(1);
numbers.push(2);
Stack<std::string> names;
names.push("first");
names.push("second");
std::cout << numbers.pop() << " " << names.pop() << " " << names.size() << "\n";
return 0;
}2 second 1
For a class template, the type is written in angle brackets: Stack<int>.
Concepts (C++20)
When a template is used with a type that does not fit, the error messages can be long and confusing. C++20 concepts state the requirements up front, so the error is short and clear:
#include <concepts>
#include <iostream>
template <std::integral T>
T sumTo(T n) {
T total = 0;
for (T i = 1; i <= n; i++) total += i;
return total;
}
int main() {
std::cout << sumTo(100) << "\n";
std::cout << sumTo(10LL) << "\n";
return 0;
}5050 55
sumTo(2.5) now fails with a message saying double does not satisfy std::integral.
Where templates live
The compiler must see a template's full code wherever it is used, so templates are written entirely in header files, not split into a declaration and a .cpp definition like ordinary functions.
Common mistakes
- With
template <typename T> T maxOf(T a, T b), the callmaxOf(3, 4.5)does not compile:Tcannot beintanddoubleat once. WritemaxOf<double>(3, 4.5)to choose the type yourself. - Error messages from templates can be long. Read the first error line: it names the type that does not support the operation you used.
Practice exercises
- Write a template
swapValues(T& a, T& b)and use it withintandstd::string. - Write a template
averagefor a vector of any number type. - Write a class template
Pair<A, B>with two members and a method that prints them.