The repetition problem

Consider a utility function that squares a number:

int square(int value)
{
    return value * value;
}

This works for integers. But your physics simulation needs to square floating-point velocities:

double square(double value)
{
    return value * value;
}

And your graphics code works with long double for precision:

long double square(long double value)
{
    return value * value;
}

Three functions with identical logic. The only difference is the type. Adding support for float or unsigned long means writing yet another copy. This violates the DRY (Don't Repeat Yourself) principle and creates a maintenance burden where a bug fix must be applied to every copy.

Worse, if someone wants to use square with a custom numeric type you didn't anticipate, they can't without modifying your code.

Templates generate code for you

A template is a recipe that tells the compiler how to generate functions automatically. You write the recipe once, and the compiler creates specialized versions for each type you need.

Consider a waffle maker. You pour in different batters and get different waffles - blueberry, chocolate chip, plain - but the waffle maker itself stays the same. A template works similarly: same pattern, different types, multiple results.

Template (recipe) + Type (ingredient) = Generated function (result)

The compiler does the repetitive work of creating type-specific functions. You maintain one template instead of dozens of nearly-identical functions.

Function templates

A function template defines a pattern for generating functions. The template you write is the primary template. Functions the compiler generates from it are instantiated functions or template instances.

Instead of writing concrete types like int or double, you use placeholder types called type template parameters. When the template is used, the compiler substitutes actual types for these placeholders.

Converting a function to a template

Start with our square function:

int square(int value)
{
    return value * value;
}

The type int appears twice: parameter type and return type. To make this a template, we replace int with a placeholder.

Replace the type with a placeholder:

T square(T value)  // Error: T is undefined
{
    return value * value;
}

This fails because T has no meaning to the compiler yet.

Declare the template parameter:

template <typename T>
T square(T value)
{
    return value * value;
}

The line template <typename T> is the template parameter declaration. It tells the compiler:

  • This is a template definition
  • T represents an unspecified type that will be provided later

Now the compiler can generate square functions for any type where multiplication is defined.

Syntax breakdown

template <typename T>    // Template parameter declaration
T square(T value)        // Function signature using T
{
    return value * value;  // Function body using T
}
Component Purpose
template Marks this as a template definition
<typename T> Declares T as a type placeholder
T square(T value) Return type and parameter use the placeholder

The keyword class can substitute for typename in the declaration:

template <class T>    // Equivalent to typename T
T square(T value)
{
    return value * value;
}

Both work identically. Prefer typename because it clarifies that T can be any type, not just class types.

Naming conventions

Single uppercase letters starting with T are conventional for simple templates:

template <typename T>           // Single type parameter
template <typename T, typename U>  // Two type parameters

When the purpose isn't obvious, use descriptive names:

template <typename NumericType>
template <typename ElementType>
template <typename Allocator>

The standard library often uses descriptive names like InputIterator or Compare to indicate requirements the type must satisfy.

Templates are recipes, not functions

A template by itself generates no code. It's a set of instructions waiting to be used. Only when you call the template with a specific type does the compiler generate an actual function.

This has an important implication: templates can work with types that didn't exist when the template was written. Your square template will work with a BigInteger class someone writes next year, as long as that class supports multiplication.

Why templates matter

Templates provide:

  • Code reuse: Write logic once, use with many types
  • Type safety: Compiler generates type-correct code
  • Future compatibility: Works with types created later
  • Zero overhead: Generated functions are as efficient as hand-written ones

The next lesson covers how the compiler instantiates templates and how to call template functions.

Summary

  • Function template: A pattern for generating type-specific functions
  • Type template parameter: A placeholder (like T) for an unspecified type
  • Primary template: The template definition you write
  • Instantiated function: A concrete function generated from the template
  • Use template <typename T> before the function definition
  • Replace concrete types with the placeholder in the signature and body
  • typename and class are interchangeable (prefer typename)
  • Templates generate code only when used with specific types