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
}
Output

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';
}
Output

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';
}
Output

42

#include <concepts>
#include <string>

template <std::integral T>
T twice(T x) { return x * 2; }

int main() {
    twice(std::string("a"));
}
Output

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';
}
Output

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';
}
Output

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.
Note: Use ready templates of the STL first, and write your own when you see repeated code for many types. Next: C++ Enums, Structs and Namespaces.

Congratulations! Chapter Finished. Learn more about the similar topics:
Exercises & Assignments
No Content Found.
Interview Questions & Answers
No Content Found.