Templates

Generic programming with function and class templates

📐 What are Templates?

Templates enable generic programming in C++. They allow you to write code that works with any data type, creating reusable functions and classes that adapt to different types at compile time.


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

Key Template Concepts

🔧

Function Templates

Function templates enable you to write a single, generic function that works with multiple data types, using placeholder types specified with the template<typename T> syntax. The compiler generates type-specific versions as needed. For instance, a getMax(T a, T b) template can compare integers, doubles, characters, or even custom objects, returning the larger value. This promotes code reuse, reduces duplication, and ensures type safety. Templates are a cornerstone of generic programming in C++, allowing operations like swapping pairs or printing arrays to be defined once yet applied universally.

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

Class Templates

Generic classes for different data types

template<typename T>
class Stack { /* ... */ };

Compile-time

Templates are resolved at compile time

Stack<int> intStack;
Stack<string> stringStack;
🎯

Type Safety

Maintains type safety while being generic

vector<int> numbers;
vector<string> words;

🔹 Function Templates

Function templates enable you to write a single, generic function that works with multiple data types, using placeholder types specified with the template<typename T> syntax. The compiler generates type-specific versions as needed. For instance, a getMax(T a, T b) template can compare integers, doubles, characters, or even custom objects, returning the larger value. This promotes code reuse, reduces duplication, and ensures type safety. Templates are a cornerstone of generic programming in C++, allowing operations like swapping pairs or printing arrays to be defined once yet applied universally.

#include <iostream>
using namespace std;

// Basic function template
template<typename T>
T getMax(T a, T b) {
    return (a > b) ? a : b;
}

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

// Template with non-type parameter
template<typename T, int SIZE>
void printArray(T arr[]) {
    for (int i = 0; i < SIZE; i++) {
        cout << arr[i] << " ";
    }
    cout << endl;
}

int main() {
    // Automatic type deduction
    cout << getMax(10, 20) << endl;        // int
    cout << getMax(3.14, 2.71) << endl;    // double
    cout << getMax('a', 'z') << endl;      // char
    
    // Explicit type specification
    cout << getMax<double>(10, 20.5) << endl;
    
    // Multiple template parameters
    printPair(42, "Hello");
    printPair(3.14, true);
    
    // Non-type template parameter
    int numbers[] = {1, 2, 3, 4, 5};
    printArray<int, 5>(numbers);
    
    return 0;
}

Output:

20

3.14

z

20.5

First: 42, Second: Hello

First: 3.14, Second: 1

1 2 3 4 5

🔹 Class Templates

Create generic classes that can work with different data types:

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

// Basic class template
template<typename T>
class Box {
private:
    T content;
    
public:
    Box(T item) : content(item) {}
    
    T getContent() const {
        return content;
    }
    
    void setContent(T item) {
        content = item;
    }
    
    void display() const {
        cout << "Box contains: " << content << endl;
    }
};

// Class template with multiple parameters
template<typename T, int CAPACITY>
class FixedArray {
private:
    T data[CAPACITY];
    int size;
    
public:
    FixedArray() : size(0) {}
    
    bool add(T item) {
        if (size < CAPACITY) {
            data[size++] = item;
            return true;
        }
        return false;
    }
    
    T get(int index) const {
        if (index >= 0 && index < size) {
            return data[index];
        }
        throw out_of_range("Index out of range");
    }
    
    int getSize() const { return size; }
    int getCapacity() const { return CAPACITY; }
};

int main() {
    // Using Box template with different types
    Box<int> intBox(42);
    Box<string> stringBox("Hello Templates!");
    Box<double> doubleBox(3.14159);
    
    intBox.display();
    stringBox.display();
    doubleBox.display();
    
    // Using FixedArray template
    FixedArray<int, 5> numbers;
    numbers.add(10);
    numbers.add(20);
    numbers.add(30);
    
    cout << "Array size: " << numbers.getSize() << endl;
    cout << "Array capacity: " << numbers.getCapacity() << endl;
    cout << "First element: " << numbers.get(0) << endl;
    
    return 0;
}

Output:

Box contains: 42

Box contains: Hello Templates!

Box contains: 3.14159

Array size: 3

Array capacity: 5

First element: 10

🔹 Template Specialization Preview

Templates can be specialized for specific types:

// Generic template
template<typename T>
class Printer {
public:
    void print(T value) {
        cout << "Generic: " << value << endl;
    }
};

// Specialized template for strings
template<>
class Printer<string> {
public:
    void print(string value) {
        cout << "String: \"" << value << "\"" << endl;
    }
};

int main() {
    Printer<int> intPrinter;
    Printer<string> stringPrinter;
    
    intPrinter.print(42);           // Uses generic template
    stringPrinter.print("Hello");   // Uses specialized template
    
    return 0;
}

Output:

Generic: 42

String: "Hello"

🧠 Test Your Knowledge

When are templates resolved in C++?