Home

What are templates?

Templates look like this:

template<typename T>
T neg(T x) {
  return (x >= 0) ? x : -x;
}

Templates are, as the name suggests, templates for creating something:

  1. function,
  2. class, or
  3. variable.

They help you write more generic code, without repeating yourself.

Why do you want them?

Imagine implementing a negation function

double neg(double x) {
  return -x;
}

int neg(int x) {
  return -x;
}

You can have function overloading, but you will have to implement the bodies of all the functions.

Templates allow you to write the body of the function in a single place (once), and only when a template is used, the compiler will instantiate the template.

Thus, in this example:

template<typename T>
T neg(T x) {
  return -x;
}

A function neg for a specific type T will only be created for each T that is used in the program. I.e. if somewhere we call neg(int), only that will be created, neg(double) will not be created.

Using a function template

You specify the type for which the template will be instantiated with inside of angle brackets <>:

int main() {
  printf("%d\n", neg<int>(-1));
}

In some cases the compiler can deduce the type of T inside a neg<T>, based on the function parameter.

Class template

The syntax looks similar, for a node in a binary tree1:

template <typename T>
struct Node {
  T data;
  Node<T> *left;
  Node<T> *right;
};

Variable templates (C++14)

Again the syntax looks quite similar:

template<typename T>
const bool size = sizeof(T);

In a test file in V8, we make use of variable templates to set different values for float and double.

Alias templates

Like typedef, we can use the keyword using to create an alias template, these are templates for creating new aliases:

// compile with C++17 using '-std=c++17' in Clang.
using node_int = Node<int>
template<typename T> using node = Node<T>

int main() {
  static_assert(is_same_v<node_int, Node<int>>);
  static_assert(is_same_v<node<double>, Node<double>);
}

Debugging template types

Templates are well known to be hard to debug, and stack traces involving templates are multiple pages long.

There are some tips to debug the instantiated type of a template function.

template<typename T>
T neg(T x) {
  puts(__PRETTY_FUNCTION__);
  return -x;
}

This prints a pretty-printed function name, that includes the template type information. 2

Template argument deduction

High level look at how template argument deduction works:

The details can be found in this cppreference.com article on Template argument deduction

Some examples:

template<typename To, typename From> To convert(From f);
 
void g(double d) 
{
    int i = convert<int>(d);    // calls convert<int, double>(double)
    char c = convert<char>(d);  // calls convert<char, double>(double)
    int(*ptr)(float) = convert; // instantiates convert<int, float>(float)
}

For the first call:

Similar, the for the second call:

For the last call, we want a function that

Here is an example that shows a conflict due to no implicit conversions:

template<typename T>
void g(T x, T y);

int main() {
  g(1, 1u);
}
// note: candidate template ignored: deduced conflicting types for parameter 'T' ('int' vs. 'unsigned int')

Once a template argument is explicitly specialized, there is no need to deduce it at all.

Template specialization

This is used when you want to have different bodies for a template when it is instantiated with a particular type:

template<typename T>
int size() { return sizeof(T); }

template<>
int size<void>() { return 1; }

Here, we defined a specialization for when T is void, and do something different.

When a specialization is defined for all template arguments, it is a full specialization.

References


  1. We can omit <T> in the declaration of left and right, the compiler can deduce the template argument, which is necessarily <T> since we are inside of a template:

    template <typename T>
    struct Node {
      T data;
      Node *left;
      Node *right;
    };

    More on template argument deduction later.↩︎

  2. This looks to be a GCC specific extension, I don’t see this listed on Clang’s Language Extensions page.↩︎