Templates

Intermediate

Introduction

If you wrote larger for int, then again for double and again for string, you would have three copies of the same idea to maintain. A template lets you write it once and leave the type to be filled in.

How it works

template <typename T> introduces a placeholder for a type. The compiler looks at each use, works out T, and generates a real function for that type. This happens at compile time, so there is no cost when the program runs.

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

Giving the type yourself. Usually the compiler can work it out. larger<string>("apple", "pear") states it, because quoted text would otherwise be treated as a character pointer.

Requirements. The template only works for types that support what it does. larger needs >, so using it with a type that has no > is a compile error, often with a long message. Read the first line of that message, because it names the problem.

Class templates are written the same way. Every use of the class states the type:

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

template <typename T>
class Box {
    T value;

public:
    Box(T v) : value(v) {}
    T get() const {
        return value;
    }
};

int main() {
    Box<int> a(5);
    Box<string> b("hello");
    cout << a.get() << " " << b.get() << endl;
    return 0;
}
Open in compiler

The standard containers are class templates, which is why you write vector<int>.

Syntax

template <typename T>
void show(const T& value) {
    cout << value << endl;
}

template <typename K, typename V>
class Entry { ... };

Example

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

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

int main() {
    cout << larger(3, 9) << endl;
    cout << larger(2.5, 1.5) << endl;
    cout << larger<string>("apple", "pear") << endl;
    return 0;
}
Open in compiler

Line by line

  1. template <typename T> says that T is a type to be decided later.
  2. larger(3, 9) makes the compiler create a version for int.
  3. larger(2.5, 1.5) creates a version for double.
  4. larger<string> states the type, so the quoted text becomes strings.
  5. "pear" is larger than "apple", because strings compare in alphabetical order.

Expected result

9
2.5
pear

In the real world

The whole standard library is built on templates, which is why one sort works on almost any kind of data. They give the speed of hand-written code with the convenience of generic code.

Common mistakes

  • Mixing types, as in larger(1, 2.5), because T cannot be two things.
  • Putting a template's body in a .cpp file apart from its uses. Templates usually live in headers.
  • Being scared by long error messages. Start at the first line.
  • Using a template when a simple function would do.

Best practices

  • Use a template only when you truly need several types.
  • Take large values by const T&.
  • Give each type parameter a short, clear name.

Summary

Templates are patterns for functions and classes. The compiler makes real code for each type used, with no cost at run time. A type must support what the template asks of it.

Quick check

1. When is code generated from a template?
2. What does larger(1, 2.5) do with a single type parameter T?
3. Which of these is a class template?
4. What happens if T does not support an operator the template uses?

Key takeaways

  • A template avoids repeating the same code for each type.
  • The type must support the operations the template uses.
  • vector and map are themselves templates.

Practice problems for this lesson

My notes

Write down what you want to remember from this lesson. Notes are saved on this device as you type, and appear on your dashboard.