</>
Skip to content
C++ lessons (32/42)

C++ — Templates

Function templates

#include <iostream>
using namespace std;

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

int main() {
    cout << maximum(3, 7) << endl;       // 7 (int)
    cout << maximum(3.14, 2.72) << endl; // 3.14 (double)
    cout << maximum('a', 'z') << endl;   // z (char)
    return 0;
}

Multiple template parameters

#include <iostream>
using namespace std;

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

int main() {
    cout << add(3, 4) << endl;         // 7
    cout << add(3.14, 2) << endl;      // 5.14
    cout << add(string("Hello"), string(" World")) << endl;
    return 0;
}

Class templates

#include <iostream>
using namespace std;

template <typename T>
class Stack {
private:
    static const int MAX = 100;
    T data[MAX];
    int top;

public:
    Stack() : top(-1) {}

    void push(T value) {
        if (top < MAX - 1) {
            data[++top] = value;
        }
    }

    T pop() {
        if (top >= 0) {
            return data[top--];
        }
        throw runtime_error("Stack underflow");
    }

    bool isEmpty() const { return top == -1; }
    int size() const { return top + 1; }
};

int main() {
    Stack<int> intStack;
    intStack.push(10);
    intStack.push(20);
    cout << intStack.pop() << endl; // 20

    Stack<string> strStack;
    strStack.push("Hello");
    strStack.push("World");
    cout << strStack.pop() << endl; // World

    return 0;
}

Non-type template parameters

#include <iostream>
using namespace std;

template <typename T, int N>
class Array {
private:
    T data[N];

public:
    void set(int index, T value) {
        if (index >= 0 && index < N) {
            data[index] = value;
        }
    }

    T get(int index) const {
        return data[index];
    }

    int size() const { return N; }
};

int main() {
    Array<int, 5> arr;
    for (int i = 0; i < 5; i++) {
        arr.set(i, i * 10);
    }

    for (int i = 0; i < arr.size(); i++) {
        cout << arr.get(i) << " ";
    }
    cout << endl; // 0 10 20 30 40

    return 0;
}

Template specialization

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

// Generic version
template <typename T>
bool isEqual(T a, T b) {
    return a == b;
}

// Specialization for C-strings
template <>
bool isEqual<const char *>(const char *a, const char *b) {
    return strcmp(a, b) == 0;
}

int main() {
    cout << isEqual(5, 5) << endl;                // 1 (int)
    cout << isEqual(3.14, 3.14) << endl;          // 1 (double)
    cout << isEqual("hello", "hello") << endl;    // 1 (const char*)
    cout << isEqual("hello", "world") << endl;    // 0

    return 0;
}

Variadic templates (C++11)

#include <iostream>
using namespace std;

// Base case
void print() {
    cout << endl;
}

// Recursive case
template <typename T, typename... Args>
void print(T first, Args... rest) {
    cout << first << " ";
    print(rest...);
}

int main() {
    print(1, 2, 3);           // 1 2 3
    print("Hello", 3.14, 'A'); // Hello 3.14 A
    return 0;
}

SFINAE and enable_if

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

// Only enabled for integral types
template <typename T>
typename enable_if<is_integral<T>::value, T>::type
safeDivide(T a, T b) {
    if (b == 0) throw runtime_error("Division by zero");
    return a / b;
}

// Only enabled for floating-point types
template <typename T>
typename enable_if<is_floating_point<T>::value, T>::type
safeDivide(T a, T b) {
    return a / b; // No exception for floating point
}

int main() {
    cout << safeDivide(10, 3) << endl;   // 3 (int)
    cout << safeDivide(10.0, 3.0) << endl; // 3.33333 (double)

    return 0;
}

Concept constraints (C++20)

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

template <typename T>
concept Numeric = is_arithmetic_v<T>;

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

template <typename T>
concept Printable = requires(T t) {
    { cout << t } -> same_as<ostream &>;
};

template <Printable T>
void print(T value) {
    cout << value << endl;
}

int main() {
    cout << square(5) << endl;     // 25
    cout << square(3.14) << endl;  // 9.8596
    print("Hello");
    return 0;
}

Mini Practice

Write C++ code that:

  1. Creates a function template that finds the minimum of two values
  2. Creates a class template for a simple Pair
  3. Specializes the Pair template for strings
  4. Uses a variadic template to print any number of arguments

Up Next

In the next lesson, you'll learn about STL — the Standard Template Library containers and algorithms.

Related Topics

Frequently Asked Questions about Templates

What is Templates in C++?

Templates is a fundamental concept in C++. This lesson explains it step by step with clear examples, making it easy for beginners to understand.

How do I learn Templates?

Start by reading the explanation above, then try the code examples. Practice by modifying the examples and experimenting with different values. Hands-on practice is the best way to learn Templates.

Why is Templates important in C++?

Templates is essential for C++ development. Understanding this concept will help you write better code and solve real-world problems more effectively.