11

Module 11: Templates (Generic Programming)

Chapter 11 • Advanced

60 min

Templates (Generic Programming)

Templates are one of C++'s most powerful features, enabling generic programming - writing code that works with any data type. They're the foundation of the STL and modern C++.

What are Templates?

Templates allow you to write code that works with different data types without rewriting it for each type. Think of templates as "blueprints" for generating code.

Benefits:

  • Code Reusability: Write once, use with any type
  • Type Safety: Compile-time type checking
  • Performance: No runtime overhead (code generated at compile time)
  • Flexibility: Works with user-defined types

Function Templates

Function templates allow functions to work with different types.

Basic Function Template

Syntax:

cpp.js
template <typename T>
return_type function_name(T parameter) {
    // function body
}

Example:

cpp.js
template <typename T>
T maximum(T a, T b) {
    return (a > b) ? a : b;
}

int main() {
    cout << maximum(10, 20) << endl;        // int
    cout << maximum(3.5, 2.1) << endl;      // double
    cout << maximum('a', 'z') << endl;      // char
    return 0;
}

Multiple Template Parameters

cpp.js
template <typename T, typename U>
void printPair(T first, U second) {
    cout << first << ", " << second << endl;
}

int main() {
    printPair(10, "Hello");      // int, string
    printPair(3.14, 'A');        // double, char
    return 0;
}

Template Type Deduction

Compiler automatically deduces types:

cpp.js
template <typename T>
void display(T value) {
    cout << value << endl;
}

display(10);      // T = int
display(3.14);    // T = double
display("Hello"); // T = const char*

Explicit Template Arguments

Specify types explicitly:

cpp.js
template <typename T>
T add(T a, T b) {
    return a + b;
}

int result = add<int>(5, 3);        // Explicit
double result2 = add<double>(5.5, 3.2);

Class Templates

Class templates allow classes to work with different types.

Basic Class Template

Syntax:

cpp.js
template <typename T>
class ClassName {
    // class members
};

Example:

cpp.js
template <typename T>
class Stack {
private:
    vector<T> elements;
    
public:
    void push(const T& element) {
        elements.push_back(element);
    }
    
    void pop() {
        if (!elements.empty()) {
            elements.pop_back();
        }
    }
    
    T top() const {
        return elements.back();
    }
    
    bool empty() const {
        return elements.empty();
    }
};

int main() {
    Stack<int> intStack;
    intStack.push(10);
    intStack.push(20);
    
    Stack<string> stringStack;
    stringStack.push("Hello");
    stringStack.push("World");
    
    return 0;
}

Template Member Functions

Member functions defined outside class:

cpp.js
template <typename T>
class Vector {
private:
    T x, y;
    
public:
    Vector(T x, T y);
    T getX() const;
    T getY() const;
};

// Definition outside class
template <typename T>
Vector<T>::Vector(T x, T y) : x(x), y(y) {}

template <typename T>
T Vector<T>::getX() const {
    return x;
}

Template Specialization

Provide specific implementation for particular types.

Function Template Specialization

cpp.js
template <typename T>
void print(T value) {
    cout << "Generic: " << value << endl;
}

// Specialization for strings
template <>
void print<string>(string value) {
    cout << "String: " << value << endl;
}

int main() {
    print(10);           // Generic
    print(3.14);         // Generic
    print(string("Hi")); // String specialization
    return 0;
}

Class Template Specialization

cpp.js
template <typename T>
class Container {
public:
    void display() {
        cout << "Generic container" << endl;
    }
};

// Specialization for int
template <>
class Container<int> {
public:
    void display() {
        cout << "Integer container" << endl;
    }
};

Template Parameters

Non-Type Template Parameters

Templates can take values, not just types:

cpp.js
template <typename T, int N>
class Array {
private:
    T data[N];
    
public:
    T& operator[](int index) {
        return data[index];
    }
    
    int size() const {
        return N;
    }
};

int main() {
    Array<int, 5> arr;  // Array of 5 integers
    Array<double, 10> arr2;  // Array of 10 doubles
    return 0;
}

Default Template Parameters

cpp.js
template <typename T = int, int N = 10>
class Buffer {
    T data[N];
};

int main() {
    Buffer<> buffer1;        // Uses defaults: int, 10
    Buffer<double> buffer2;  // double, 10
    Buffer<int, 20> buffer3; // int, 20
    return 0;
}

Template Constraints (C++20)

Concepts (C++20) allow constraining template parameters:

cpp.js
template <typename T>
concept Addable = requires(T a, T b) {
    a + b;
};

template <Addable T>
T add(T a, T b) {
    return a + b;
}

Variadic Templates (C++11)

Templates that accept variable number of arguments:

cpp.js
template <typename... Args>
void print(Args... args) {
    ((cout << args << " "), ...);  // C++17 fold expression
}

int main() {
    print(1, 2.5, "Hello", 'A');  // Works with any number of args
    return 0;
}

Common Template Patterns

Pattern 1: Generic Swap

cpp.js
template <typename T>
void swap(T& a, T& b) {
    T temp = a;
    a = b;
    b = temp;
}

Pattern 2: Generic Find

cpp.js
template <typename Iterator, typename T>
Iterator find(Iterator first, Iterator last, const T& value) {
    for (Iterator it = first; it != last; ++it) {
        if (*it == value) {
            return it;
        }
    }
    return last;
}

Pattern 3: Generic Container

cpp.js
template <typename T>
class Queue {
private:
    vector<T> elements;
    
public:
    void enqueue(const T& item) {
        elements.push_back(item);
    }
    
    void dequeue() {
        if (!elements.empty()) {
            elements.erase(elements.begin());
        }
    }
    
    T front() const {
        return elements.front();
    }
};

Template Metaprogramming (Advanced)

Templates can be used for compile-time computation:

cpp.js
template <int N>
struct Factorial {
    static const int value = N * Factorial<N - 1>::value;
};

template <>
struct Factorial<0> {
    static const int value = 1;
};

int main() {
    cout << Factorial<5>::value << endl;  // Computed at compile time!
    return 0;
}

Best Practices

  1. Use templates for code that works with multiple types
  2. Prefer typename over class in template declarations
  3. Use const references for template parameters when possible
  4. Document template requirements (what operations T must support)
  5. Use concepts (C++20) for better error messages
  6. Avoid unnecessary template specialization
  7. Keep templates in header files (usually)
  8. Use SFINAE (Substitution Failure Is Not An Error) carefully

Common Mistakes

  • ❌ Forgetting to include template definition in header
  • ❌ Template instantiation errors (unclear error messages)
  • ❌ Circular template dependencies
  • ❌ Not understanding template instantiation
  • ❌ Overusing templates when not needed
  • ❌ Template specialization conflicts

Next Module

In Module 12, we'll learn about Smart Pointers - modern C++ memory management that eliminates many common pointer errors!

Hands-on Examples

Function Templates

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

// Function template for maximum
template <typename T>
T maximum(T a, T b) {
    return (a > b) ? a : b;
}

// Function template for swap
template <typename T>
void swapValues(T& a, T& b) {
    T temp = a;
    a = b;
    b = temp;
}

// Function template with multiple types
template <typename T, typename U>
void printPair(T first, U second) {
    cout << "Pair: " << first << ", " << second << endl;
}

int main() {
    // Works with integers
    cout << "Max of 10 and 20: " << maximum(10, 20) << endl;
    
    // Works with doubles
    cout << "Max of 3.5 and 2.1: " << maximum(3.5, 2.1) << endl;
    
    // Works with characters
    cout << "Max of 'a' and 'z': " << maximum('a', 'z') << endl;
    
    // Works with strings
    cout << "Max of 'apple' and 'banana': " << maximum(string("apple"), string("banana")) << endl;
    
    // Swap function
    int x = 10, y = 20;
    cout << "\nBefore swap: x = " << x << ", y = " << y << endl;
    swapValues(x, y);
    cout << "After swap: x = " << x << ", y = " << y << endl;
    
    // Multiple types
    printPair(10, "Hello");
    printPair(3.14, 'A');
    
    return 0;
}

Function templates allow you to write one function that works with multiple types. The compiler generates specific versions for each type used. Use typename or class keyword. Templates provide type safety and code reuse without performance overhead.