Intermediate · Templates, smart pointers and move semantics · topic 26 of 42 · 35 min
Templates: code for any type
Explanation
Why templates?
Suppose you write int largest(int a, int b). Tomorrow you need it for double, then for std::string. Copying the function for every type is boring and error-prone. A template lets you write the logic once, with the type as a parameter. This style is called generic programming.
#include <iostream>
#include <string>
template <typename T>
T largest(T a, T b) {
return (a > b) ? a : b;
}
int main() {
std::cout << largest(3, 9) << '\n'; // 9 (T = int)
std::cout << largest(2.5, 1.5) << '\n'; // 2.5 (T = double)
std::cout << largest(std::string("apple"), std::string("mango")) << '\n'; // mango
// largest(3, 2.5); // error: T cannot be both int and double
std::cout << largest<double>(3, 2.5) << '\n'; // 3, T given explicitly
}Class templates
template <typename T>
class Box {
public:
explicit Box(T value) : value_(value) {}
const T& get() const { return value_; }
private:
T value_;
};
template <typename K, typename V>
struct Entry {
K key;
V value;
};
Box<int> b1(42);
Box<std::string> b2("parcel");
Box b3(3.5); // C++17: the compiler deduces Box<double>
Entry<std::string, int> e{"rice", 40};Non-type template parameters
template <typename T, std::size_t N>
std::size_t lengthOf(const T (&)[N]) { return N; }
int marks[7] = {};
std::cout << lengthOf(marks) << '\n'; // 7Templates live in headers
The compiler needs to see the full template body at the point where it creates a new version (for example largest<double>). That is why templates are almost always written entirely in header files, not split into .h and .cpp. Putting the body in a .cpp file usually gives "undefined reference" linker errors.
C++20 concepts: saying what T must support
#include <concepts>
template <typename T>
concept Number = std::integral<T> || std::floating_point<T>;
template <Number T>
T average(T a, T b) { return (a + b) / 2; }
// average(4, 6) == 5; average(std::string("a"), std::string("b")) -> clear error: constraints not satisfied
auto twice(std::integral auto x) { return x * 2; } // abbreviated formBefore concepts, template error messages were famous for being hundreds of lines long. A concept checks the requirement at the call and reports one clear message like "the associated constraints are not satisfied".
Common mistakes
- Mixing argument types so T cannot be deduced (largest(3, 2.5)). Convert one argument or write largest<double>(...).
- Writing a template body in a .cpp file and getting linker errors.
- Assuming T supports an operation (like > or +) that it does not. Use a concept to make the requirement explicit.
- Over-using templates for code that only ever handles one type. Generic code is harder to read; use it when it truly pays off.
Need help?
Stuck on a word or an idea? Search this course, or every course, and jump straight to the explanation or a worked question.
Try:
Watch the lesson
Templates: code for any type · 1/6
Templates let you write a function or class once and use it with many types.
Prefer a video by a teacher? Find free videos on “c++ templates function template class template concepts tutorial” on YouTube ↗
In real life: A generic "top N" function for dashboards
An analytics dashboard shows the top 5 products by sales, the top 5 cities by orders and the top 5 riders by ratings. One function template topN works for all three, whatever the item type, as long as you pass a way to compare them.
#include <algorithm>
#include <iostream>
#include <string>
#include <vector>
template <typename T, typename Compare>
std::vector<T> topN(std::vector<T> items, std::size_t n, Compare better) {
std::sort(items.begin(), items.end(), better);
if (items.size() > n) items.resize(n);
return items;
}
int main() {
std::vector<int> sales = {120, 450, 90, 300, 610};
for (int s : topN(sales, 3, [](int a, int b) { return a > b; })) std::cout << s << ' ';
std::cout << '\n'; // 610 450 300
std::vector<std::string> cities = {"Pune", "Agra", "Kochi"};
for (const auto& c : topN(cities, 2, [](const std::string& a, const std::string& b) { return a < b; })) std::cout << c << ' ';
std::cout << '\n'; // Agra Kochi
}Practice (0/4 done)
template <typename T> T add(T a, T b) { return a + b; } What is add(2, 3)?
Why does add(2, 3.5) fail to compile with the template above?
You put a template function's body in utils.cpp and only its declaration in utils.h. Other files call it with new types. What usually happens?
Write a class template Stack<T> using a std::vector<T> inside, with push, pop, top and empty. Then write a function template printAll that prints every element of any std::vector<T>. Test Stack<int> and Stack<std::string>.
Quick quiz
Read more (free)
Related help
Before this, learn
This topic builds on these. If something here feels new, open them first.
Related topics
- Smart pointers 1: std::unique_ptrTemplates, smart pointers and move semantics · same module
- Smart pointers 2: std::shared_ptr and std::weak_ptrTemplates, smart pointers and move semantics · same module
- Move semantics: steal instead of copyTemplates, smart pointers and move semantics · same module
- C++20 ranges, views and other C++20 essentialsModern C++: lambdas, C++17 and C++20 essentials
- Abstract classesJava Programming (Core to Spring Boot) · Inheritance, polymorphism and interfaces
- Jinja2 templatesLinux and Red Hat (RHCSA to RHCE) · Ansible in depth
Questions people ask about this
Open a question to see the answer and why. The first ones are this topic's own quiz, so try the quiz above before you peek.
When are templates turned into real code?
Answer: At compile time, when used with specific types
Instantiation happens during compilation.
Which standard library type is a class template?
Answer: std::vector
std::vector<T> is a class template; int, double and char are built-in types.
What does a C++20 concept do?
Answer: States compile-time requirements on template arguments, giving clearer errors
Concepts constrain templates and improve error messages.
Box b(3.5); compiles in C++17 because of…
Answer: class template argument deduction (CTAD)
CTAD deduces Box<double> from the constructor argument.
Which is a valid C++20 designated initialiser for struct P { int x; int y; };?Related
Answer: P p{.x = 1, .y = 2};
Designators use .name and must follow the declaration order.
Which include style is used for your own header file?Related
Answer: #include "myfile.h"
Quotes search your project folder first; angle brackets are for standard and system headers.
What is the template method pattern?Related
Answer: A fixed sequence of steps in a parent, with some steps filled in by subclasses
The parent owns the flow.
From Abstract classes
A hosts template using hostvars fails with an undefined variable when run with --limit web01. Why?Related
Answer: Facts for the other hosts were not gathered in this run
Gather facts for all hosts first.
From Jinja2 templates
Pass the quiz (60% or more) or mark this topic as done when you are ready.
Your progress is saved on this device only.
Next steps
- 3 more topics in “Templates, smart pointers and move semantics” before its exam: Smart pointers 1: std::unique_ptr, Smart pointers 2: std::shared_ptr and std::weak_ptr, Move semantics: steal instead of copy.
- Module 7 exam: Templates, smart pointers and move semantics · 12 questions · 25 min, without help. 4 topics of this module still to finish first.
- Then the next module, Modern C++: lambdas, C++17 and C++20 essentials. 42 topics left in the course before the final exam.
← Previous: STL algorithms: sort, find, count, accumulate and binary search