C++ Templates

Write code once for many types with function and class templates, and see how the STL is built on them.

Lesson 23 of 44Advanced18 min read

Imagine writing a function that returns the larger of two numbers. You write it for int, then you need it for double, then for string. The three versions are identical except for the type. Copying code like that is tedious and error-prone, and templates remove the need for it.

A template is a blueprint for a function or class with one or more type parameters. You write the code once, using a placeholder such as T for the type, and the compiler generates a real version for each type you use it with. This style is called generic programming, and it's one of the features that makes C++ both flexible and fast.

You've already been using templates: vector<int>, map<string, double> and unique_ptr<Player> are all class templates from the standard library. In this lesson you'll write your own function and class templates, understand what happens behind the scenes and learn how C++20 concepts make template errors readable.

Function templates

C++
template <typename T>
T biggest(T a, T b) {
    return (a > b) ? a : b;
}

biggest(3, 9);              // T is int
biggest(2.5, 1.5);          // T is double
biggest<string>("pear", "apple");
  • template <typename T> introduces a type parameter called T. (class can be used instead of typename; they mean the same here.)
  • The compiler usually deduces T from the arguments. You can also name it explicitly in angle brackets.
  • Both arguments must have the same type for T to be deduced: biggest(3, 2.5) fails because T can't be both int and double.

How templates work

A template isn't compiled on its own. When you call biggest(3, 9), the compiler instantiates a version where every T becomes int, and compiles that. Each type gets its own efficient, fully typed code, with no run-time cost compared with writing the versions by hand.

The code inside the template must make sense for every type you use. biggest needs > to work, so it's fine for numbers and strings but fails to compile for a struct without a > operator. Because templates are instantiated where they're used, their full definitions usually live in header files.

Class templates

A class can be a template too. Here's a small box that holds any type:

C++
template <typename T>
class Box {
    T value;
public:
    Box(T v) : value(v) {}
    T get() const { return value; }
};

Box<int> a(5);
Box<string> b("hello");

Templates can have several parameters, as in template <typename K, typename V>, which is exactly how std::map<K, V> is declared. Since C++17 the compiler can often deduce class template arguments too, so Box c(3.5); creates a Box<double>.

auto, constexpr and concepts

auto asks the compiler to deduce a variable's type from its value, which is especially handy with long template types: auto it = prices.find("Pen");. Function parameters can even be auto in C++20, a shorthand for a template.

constexpr marks values and functions that can be computed at compile time, such as constexpr int MAX = 100;, which is a natural partner for templates.

Template errors used to be famously long and confusing. C++20 concepts let you state what a type must support, and the compiler reports a short, clear message if it doesn't:

C++
#include <concepts>
template <std::totally_ordered T>
T biggest(T a, T b) { return (a > b) ? a : b; }

C++ Templates Syntax

Template syntax

template <typename T>
T functionName(T a, T b) { ... }

template <typename K, typename V>
class Pair {
    K key;
    V value;
public:
    Pair(K k, V v) : key(k), value(v) {}
};

functionName(1, 2);
functionName<double>(1, 2.5);
Pair<string, int> p("age", 15);

C++ Templates Summary

  • template <typename T> writes a function or class once for many types.
  • The compiler deduces T from arguments, or you can give it: f<int>(...).
  • Each type used creates its own instantiated, fully typed version at compile time.
  • Class templates like Box<T>, vector<T> and map<K, V> take type parameters in angle brackets.
  • auto deduces types; C++20 concepts state requirements and give clearer errors.

C++ Templates Examples

Each example shows the code, the output it produces and what each part does.

Example 1

One function, many types

C++
#include <iostream>
#include <string>
using namespace std;

template <typename T>
T biggest(T a, T b) {
    return (a > b) ? a : b;
}

int main() {
    cout << biggest(3, 9) << endl;
    cout << biggest(2.5, 1.5) << endl;
    cout << biggest<string>("pear", "apple") << endl;
    cout << biggest<double>(3, 2.5) << endl;
    return 0;
}
Output
9
2.5
pear
3
Line-by-line explanation
  1. Lines 5 to 8The template is written once with the placeholder T.
  2. Lines 11 to 12T is deduced as int, then double.
  3. Line 13Naming string makes the text literals compare as strings, alphabetically.
  4. Line 14Naming double lets an int and a double be compared.
Example 2

A function template over containers

C++
#include <iostream>
#include <vector>
#include <string>
using namespace std;

template <typename T>
T total(const vector<T>& items) {
    T sum{};
    for (const T& x : items) sum += x;
    return sum;
}

int main() {
    vector<int> scores = {72, 95, 61};
    vector<double> prices = {1.20, 3.50, 24.50};
    vector<string> parts = {"Code", "vra"};

    cout << total(scores) << endl;
    cout << total(prices) << endl;
    cout << total(parts) << endl;
    return 0;
}
Output
228
29.2
Codevra
Line-by-line explanation
  1. Line 8T sum{} starts at 0 for numbers and an empty string for strings.
  2. Line 9+= adds numbers and joins strings, so the same template works for both.
  3. Lines 18 to 20Three different instantiations from one template.
Example 3

A class template

C++
#include <iostream>
#include <string>
#include <vector>
using namespace std;

template <typename T>
class Stack {
    vector<T> items;
public:
    void push(const T& x) { items.push_back(x); }
    T pop() {
        T top = items.back();
        items.pop_back();
        return top;
    }
    bool empty() const { return items.empty(); }
    size_t size() const { return items.size(); }
};

int main() {
    Stack<string> history;
    history.push("home");
    history.push("shop");
    history.push("checkout");

    cout << "Pages: " << history.size() << endl;
    while (!history.empty()) {
        cout << "Back from " << history.pop() << endl;
    }
    return 0;
}
Output
Pages: 3
Back from checkout
Back from shop
Back from home
Line-by-line explanation
  1. Lines 6 to 18A stack (last in, first out) that works for any type, built on a vector.
  2. Line 21Stack<string> creates a version for strings; Stack<int> would work just as well.
  3. Lines 27 to 29Items come back out in reverse order, like a browser's back button.

Common C++ Templates Mistakes

Mixing types the template can't deduce

Don't
biggest(3, 2.5);          // error: T deduced as both int and double
Do
biggest<double>(3, 2.5);

Using a type that lacks a required operation

Don't
struct Point { int x, y; };
biggest(Point{1, 2}, Point{3, 4});   // error: no operator> for Point
Do
struct Point {
    int x, y;
    bool operator>(const Point& o) const { return x > o.x; }
};

Every operation the template uses must exist for the type you pass in.

Defining templates only in a .cpp file

Don't
// box.h declares the template, box.cpp defines it
// main.cpp: Box<int> b(1);  -> linker error
Do
// box.h: declare AND define the template in the header

The compiler needs the full template wherever it's used, so template code lives in headers.

C++ Templates in Real Projects

Templates in real C++

The entire Standard Template Library, from vector and map to sort and unique_ptr, is built from templates. Game engines use them for maths types such as Vector3<float>, and libraries like Eigen use templates to generate extremely fast numerical code. Understanding templates makes the STL's documentation and error messages much easier to read.

C++ Templates FAQ

What is template specialisation in C++?

A specialisation gives a template a different implementation for one particular type. After the general template <typename T> string describe(T), writing template <> string describe<bool>(bool b) { ... } makes calls with a bool use the special version, while every other type uses the general one. For functions, an ordinary overload is often simpler; specialisation is used more for class templates.

Can a template parameter be a number instead of a type?

Yes. A non-type template parameter such as template <typename T, int N> takes a value known at compile time. That's how std::array<int, 5> works: the size is part of the type, so it costs nothing at run time and arrays of different sizes are different types. The value must be a constant, such as a literal or a constexpr variable.

What is a variadic template?

A template that accepts any number of arguments, written with ...: template <typename... Ts> auto sum(Ts... xs) { return (xs + ...); }. The (xs + ...) is a C++17 fold expression, which applies + across every argument, so sum(1, 2, 3.5) gives 6.5. Functions like std::make_unique use variadic templates to pass any constructor arguments through.

Do templates make programs bigger or slower?

Not slower: the compiler writes a separate, fully typed version for each type you use, so the code runs as fast as if you'd written it by hand. Each of those versions takes space, though, so using a large template with many different types can make the executable bigger and compilation slower. In everyday programs the effect is small.

Can a member function template be virtual?

No. g++ reports "templates may not be 'virtual'", because a virtual function needs a single entry in the class's table of virtual functions, and a template could produce any number of versions. If you need both, make the virtual function take a fixed type, or keep the template non-virtual and have it call a virtual function internally.

C++ Templates Exercises

Answer each one and check it instantly. If you get stuck, open the hint before the answer.

Fill in the blankseasy

Complete the line that starts a function template.

 < T>
T twice(T x) { return x + x; }
Predict the outputeasy

What does this print?

template <typename T>
T twice(T x) { return x + x; }
// in main:
cout << twice(4) << " " << twice(string("ab"));
Multiple choicemedium

What happens when you call a function template with a new type?

Predict the outputmedium

What does this print?

template <typename T>
class Box {
    T v;
public:
    Box(T x) : v(x) {}
    T get() const { return v; }
};
// in main:
Box<double> b(7);
cout << b.get() / 2;
Multiple choicehard

Why are templates usually defined in header files?

C++ Templates Quiz

5 questions to check what you learned.

  1. What problem do templates solve?

    • Slow programs
    • Writing the same function or class again for different types
    • Memory leaks
    • Reading files
  2. Which of these is a class template?

    • std::vector
    • int
    • cout
    • main
  3. How do you explicitly choose the type for a function template?

    • f(int, 3, 4)
    • f(3, 4)<int>
    • int f(3, 4)
    • f<int>(3, 4)
  4. What do C++20 concepts do?

    • Replace classes
    • Run templates at run time
    • Describe requirements for template types and give clearer errors
    • Remove the need for typename
  5. Is there a run-time cost to using a function template compared with hand-written versions?

    • Yes, templates are always slower
    • No, each instantiation is ordinary compiled code for its type
    • Only for strings
    • Only in debug builds