C++ by Example
C++ by Example

Function Templates

2 min read

Function Templates

A function template defines a function that works with any type. The compiler generates a concrete version — a specialization — for each distinct set of types used at call sites.

Basic template

#include <iostream>

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

int main() {
    std::cout << max_of(3, 7)          << "\n";   // 7   — T = int
    std::cout << max_of(3.14, 2.71)    << "\n";   // 3.14 — T = double
    std::cout << max_of('a', 'z')      << "\n";   // z   — T = char
}

The compiler deduces T from the argument types. You can also specify it explicitly: max_of<double>(3, 4).

Multiple type parameters

template <typename T, typename U>
auto add(T a, U b) -> decltype(a + b) {
    return a + b;
}

std::cout << add(1, 2.5) << "\n";    // 3.5  — T=int, U=double, result=double

decltype(a + b) deduces the return type from the expression. In C++14 and later, auto return type with no trailing type achieves the same:

template <typename T, typename U>
auto add(T a, U b) {
    return a + b;
}

Template with non-type parameters

template <typename T, int N>
T dot_product(const T (&a)[N], const T (&b)[N]) {
    T result = 0;
    for (int i = 0; i < N; i++) result += a[i] * b[i];
    return result;
}

int main() {
    int u[] = {1, 2, 3};
    int v[] = {4, 5, 6};
    std::cout << dot_product(u, v) << "\n";   // 32
}

Explicit specialization

Override the template for a specific type:

template <typename T>
std::string to_str(T x) {
    return std::to_string(x);
}

template <>
std::string to_str<bool>(bool x) {
    return x ? "true" : "false";
}

std::cout << to_str(42)    << "\n";   // "42"
std::cout << to_str(true)  << "\n";   // "true"

constexpr function templates

Template functions can be constexpr, enabling compile-time evaluation:

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

static_assert(square(5) == 25);   // evaluated at compile time
constexpr double d = square(1.5); // d = 2.25, known at compile time

Variadic templates

A variadic template accepts any number of type arguments:

#include <iostream>

// Base case
void print_all() {}

// Recursive case
template <typename T, typename... Rest>
void print_all(T first, Rest... rest) {
    std::cout << first << " ";
    print_all(rest...);
}

int main() {
    print_all(1, 2.5, "hello", true);   // 1 2.5 hello 1
    std::cout << "\n";
}

C++17 fold expressions simplify this:

template <typename... Args>
void print_all(Args... args) {
    (std::cout << ... << args);   // fold over <<
}
C++ by Example
C++ by Example

Learn modern C++ through working code. Each chapter introduces one concept — variables, functions, classes, templates, smart pointers, concurrency — with a clear example, a line-by-line explanation, and notes on how it applies in real programs.

View book →