C++ Templates and Generic Programming
A template lets you write one function or class for many types. The compiler makes the real code for each type you use. This is called generic programming, and it is the base of the STL.
Function template
#include <iostream>
#include <string>
template <typename T>
T maximum(T a, T b) {
return a > b ? a : b;
}
int main() {
std::cout << maximum(3, 9) << '\n';
std::cout << maximum(2.5, 1.5) << '\n';
std::cout << maximum(std::string("pear"), std::string("apple")) << '\n';
std::cout << maximum<double>(3, 4.5) << '\n'; // give the type by hand
}
9
2.5
pear
4.5
Class template
#include <iostream>
#include <string>
template <typename T>
class Box {
public:
explicit Box(T v) : value(std::move(v)) {}
T get() const { return value; }
private:
T value;
};
int main() {
Box<int> a(25);
Box<std::string> b("C++");
Box c(3.5); // the type is found automatically (C++17)
std::cout << a.get() << ' ' << b.get() << ' ' << c.get() << '\n';
}
25 C++ 3.5
Concepts (C++20): say what the type must do
Without a rule, a template gives long error messages when you use a wrong type. A concept makes the rule clear:
#include <concepts>
#include <iostream>
template <std::integral T>
T twice(T x) { return x * 2; }
int main() {
std::cout << twice(21) << '\n';
}
42
#include <concepts>
#include <string>
template <std::integral T>
T twice(T x) { return x * 2; }
int main() {
twice(std::string("a"));
}
Wrong.cpp: In function 'int main()':
Wrong.cpp:8:10: error: no matching function for call to 'twice(std::string)'
8 | twice(std::string("a"));
| ~~~~~^~~~~~~~~~~~~~~~~~
• there is 1 candidate
• candidate 1: 'template<class T> requires integral<T> T twice(T)'
Wrong.cpp:5:3:
5 | T twice(T x) { return x * 2; }
| ^~~~~
• template argument deduction/substitution failed:
• constraints not satisfied
In file included from Wrong.cpp:1:
• /usr/local/include/c++/16.2.0/concepts: In substitution of 'template<class T> requires integral<T> T twice(T) [with T = std::__cxx11::basic_string<char>]':
• required from here
Wrong.cpp:8:10:
8 | twice(std::string("a"));
| ~~~~~^~~~~~~~~~~~~~~~~~
• required for the satisfaction of 'integral<T>' [with T = std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >]
/usr/local/include/c++/16.2.0/concepts:109:13:
109 | concept integral = is_integral_v<_Tp>;
| ^~~~~~~~
• the expression 'is_integral_v<_Tp> [with _Tp = std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >]' evaluated to 'false'
/usr/local/include/c++/16.2.0/concepts:109:24:
109 | concept integral = is_integral_v<_Tp>;
| ^~~~~~~~~~~~~~~~~~
Variadic templates
A template can take any number of arguments:
#include <iostream>
template <typename... Args>
auto sum(Args... args) {
return (args + ...); // a fold expression (C++17)
}
int main() {
std::cout << sum(1, 2, 3, 4) << ' ' << sum(1.5, 2.5) << '\n';
}
10 4
Specialization
#include <iostream>
#include <string>
template <typename T>
std::string describe(T) { return "something"; }
template <>
std::string describe<int>(int) { return "an int"; }
int main() {
std::cout << describe(5) << ", " << describe(2.5) << '\n';
}
an int, something
Good to know
- Templates are defined in header files, because the compiler needs the full code to make the types.
- They do not cost anything at run time, but they can make the compile time longer.
| Exercises & Assignments |
|---|
| No Content Found. |
| Interview Questions & Answers |
|---|
| No Content Found. |