C++ by Example
C++ by Example

Class Templates

2 min read

Class Templates

A class template defines a family of classes. The standard library is built on class templates: std::vector<T>, std::map<K,V>, std::pair<T,U> are all instantiations of class templates.

Basic class template

#include <iostream>
#include <stdexcept>

template <typename T>
class Stack {
    T data[256];
    int top = 0;

public:
    void push(const T& value) {
        if (top >= 256) throw std::overflow_error("stack full");
        data[top++] = value;
    }

    T pop() {
        if (top == 0) throw std::underflow_error("stack empty");
        return data[--top];
    }

    T& peek()       { return data[top - 1]; }
    bool empty() const { return top == 0; }
    int  size()  const { return top; }
};

int main() {
    Stack<int> s;
    s.push(10); s.push(20); s.push(30);
    std::cout << s.pop() << "\n";   // 30
    std::cout << s.peek() << "\n";  // 20

    Stack<std::string> words;
    words.push("hello");
    words.push("world");
    std::cout << words.pop() << "\n";   // world
}

For class templates, the type must be specified explicitly: Stack<int>, not Stack. C++17 adds class template argument deduction (CTAD) which can infer it from constructor arguments.

Class template argument deduction (CTAD)

template <typename T>
class Box {
    T value;
public:
    Box(T v) : value(v) {}
    T get() const { return value; }
};

Box b(42);          // T deduced as int — C++17
Box s("hello");     // T deduced as const char*

Member function definitions outside the class

For large classes, method definitions are often placed outside the class body:

template <typename T>
class Queue {
    std::vector<T> data;
public:
    void enqueue(const T& v);
    T dequeue();
};

template <typename T>
void Queue<T>::enqueue(const T& v) {
    data.push_back(v);
}

template <typename T>
T Queue<T>::dequeue() {
    T front = data.front();
    data.erase(data.begin());
    return front;
}

Template definitions must be visible at the point of instantiation, so they typically live in header files.

Partial specialization

You can specialize a class template for a subset of types:

// Primary template
template <typename T>
class Storage {
    T value;
public:
    Storage(T v) : value(v) {}
    T get() const { return value; }
};

// Specialization for pointer types
template <typename T>
class Storage<T*> {
    T* ptr;
public:
    Storage(T* p) : ptr(p) {}
    T get() const { return *ptr; }
};

Function templates do not support partial specialization — use overloads or class templates to work around this.

Template template parameters

A template can take another template as a parameter:

template <typename T, template <typename> class Container>
class Adapter {
    Container<T> data;
public:
    void add(const T& v) { data.push_back(v); }
    std::size_t size() const { return data.size(); }
};

Adapter<int, std::vector> a;
a.add(1); a.add(2);
std::cout << a.size() << "\n";   // 2
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 →