Site

Templates — Generic Programming

Tutorial 10.0  •  C++ / Learn

10.0 What This Teaches

Templates let you write code that works with any type without sacrificing type safety. This tutorial covers:

10.1 Function Templates

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

max_of(3, 7);                             // T = int
max_of(2.5, 1.8);                         // T = double
max_of(std::string("apple"), std::string("fig")); // T = std::string
The compiler deduces T from the argument types. You can also specify it explicitly: max_of<double>(3, 7.0). The template generates a separate function for each distinct type used - this is called instantiation.

10.2 Multiple Type Parameters

template<typename T, typename U>
void print_pair(T first, U second) {
    std::cout << first << ", " << second << "\n";
}

print_pair(42, 3.14);
print_pair(std::string("name"), 100);
Each template parameter is deduced independently from its argument.

10.3 Class Templates

template<typename T>
class Stack {
public:
    void push(const T& val) { data_.push_back(val); }
    void pop()               { data_.pop_back(); }
    const T& top() const     { return data_.back(); }
    bool empty() const       { return data_.empty(); }
private:
    std::vector<T> data_;
};

Stack<int> si;
si.push(1); si.push(2);
std::cout << si.top();   // 2

Stack<std::string> ss;
ss.push("hello");
Class templates must have the type argument specified explicitly: Stack<int>. Class Template Argument Deduction (CTAD, C++17) can infer it in some cases, but explicit is clearer.

10.4 Compile-time Branching with if constexpr

#include <type_traits>

template<typename T>
std::string type_label() {
    if constexpr (std::is_integral_v<T>)           return "integral";
    else if constexpr (std::is_floating_point_v<T>) return "floating point";
    else                                             return "other";
}

type_label<int>();    // "integral"
type_label<double>(); // "floating point"
if constexpr evaluates the condition at compile time and only compiles the matching branch. This lets template code behave differently for different types without runtime overhead.

10.5 Example - All Together

// Templates - function templates, class templates, type deduction.

#include <iostream>
#include <vector>
#include <string>
#include <type_traits>

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

template<typename T>
class Stack {
public:
    void push(const T& val) { data_.push_back(val); }
    void pop()              { data_.pop_back(); }
    const T& top() const   { return data_.back(); }
    bool empty() const     { return data_.empty(); }
private:
    std::vector<T> data_;
};

template<typename T>
std::string type_label() {
    if constexpr (std::is_integral_v<T>)            return "integral";
    else if constexpr (std::is_floating_point_v<T>) return "floating point";
    else                                             return "other";
}

int main() {
    std::cout << max_of(3, 7)    << "\n";   // 7
    std::cout << max_of(2.5, 1.8) << "\n";  // 2.5

    Stack<int> s;
    s.push(10); s.push(20); s.push(30);
    while (!s.empty()) { std::cout << s.top() << " "; s.pop(); }
    std::cout << "\n";

    std::cout << type_label<int>()    << "\n";
    std::cout << type_label<double>() << "\n";
    std::cout << type_label<std::string>() << "\n";
    return 0;
}
7
2.5
30 20 10
integral
floating point
other

10.6 Exercise

Exercise
  • Write a function template sum(const std::vector<T>& v) that returns the sum of all elements. Test it with int and double vectors.
  • Write a class template Pair<T, U> with public members first and second and a constructor that takes both. Add a swap() method that swaps the values (only works when T == U).
  • Use if constexpr with std::is_same_v<T, std::string> to write a template function that prints the length of a string or the absolute value of a number.

10.7 Common Mistakes

Template definition must be in the header

Templates are instantiated at compile time when the type is known. If you put the template definition in a .cpp file, other translation units cannot instantiate it. Put template definitions in header (.h) files or inline in the class body.

Type does not support the required operations

max_of(std::vector<int>{1,2}, std::vector<int>{3,4});
// error: operator> not defined for vector
Templates fail to compile if the type argument lacks required operations. The error messages can be long. C++20 Concepts improve diagnostics.

Ambiguity between template and overload

template<typename T> void f(T x) {}
void f(int x) {}   // ok: non-template is preferred for exact match
f(42);             // calls the non-template version

10.8 Key Terms

TermMeaning
template<typename T>Declares a function or class parameterized on type T
instantiationCompiler generates a concrete version of the template for a specific type
type deductionCompiler infers T from the argument types
class templateA class parameterized on one or more types
if constexprCompile-time conditional; only the matching branch is compiled
std::is_integral_v<T>Compile-time boolean: true if T is an integer type
CTADClass Template Argument Deduction (C++17): infer template args from constructor args