Docs

README

Class Templates in C++

Table of Contents


Basic Class Templates

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

Box<int> intBox(42);
Box<string> strBox("Hello");
Box<double> dblBox(3.14);

Member Functions

Inside Class

template<typename T>
class Stack {
    vector<T> data;
public:
    void push(const T& value) {
        data.push_back(value);
    }

    T pop() {
        T top = data.back();
        data.pop_back();
        return top;
    }

    bool empty() const {
        return data.empty();
    }
};

Outside Class

template<typename T>
class MyClass {
    T value;
public:
    MyClass(T v);
    T getValue() const;
    void setValue(T v);
};

template<typename T>
MyClass<T>::MyClass(T v) : value(v) {}

template<typename T>
T MyClass<T>::getValue() const { return value; }

template<typename T>
void MyClass<T>::setValue(T v) { value = v; }

Non-Type Parameters

template<typename T, size_t Size>
class Array {
    T data[Size];
public:
    T& operator[](size_t i) { return data[i]; }
    const T& operator[](size_t i) const { return data[i]; }
    constexpr size_t size() const { return Size; }
};

Array<int, 10> arr;
arr[0] = 42;

// Size is compile-time constant
Array<double, 100> bigArr;

Default Template Arguments

template<typename T = int, size_t Size = 10>
class Buffer {
    T data[Size];
public:
    size_t size() const { return Size; }
};

Buffer<> buf1;              // int, size 10
Buffer<double> buf2;        // double, size 10
Buffer<char, 256> buf3;     // char, size 256

Static Members

Each instantiation has its own static members:

template<typename T>
class Counter {
public:
    static int count;
    Counter() { ++count; }
    ~Counter() { --count; }
};

template<typename T>
int Counter<T>::count = 0;

Counter<int> a, b, c;
cout << Counter<int>::count;     // 3

Counter<double> d;
cout << Counter<double>::count;  // 1 (separate counter!)

Template Specialization

Full Specialization

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

// Specialization for bool
template<>
class Printer<bool> {
public:
    void print(const bool& value) {
        cout << (value ? "true" : "false") << endl;
    }
};

Partial Specialization

// Primary template
template<typename T>
class Container {
    T value;
public:
    void show() { cout << "Value: " << value << endl; }
};

// Partial specialization for pointers
template<typename T>
class Container<T*> {
    T* ptr;
public:
    void show() { cout << "Pointer to: " << *ptr << endl; }
};

Best Practices

✅ Do

// 1. Keep template code in headers
// myclass.h
template<typename T>
class MyClass {
    // Full implementation here
};

// 2. Use typename for dependent types
template<typename T>
class Container {
    typename T::value_type item;  // T::value_type is a type
};

// 3. Provide type aliases
template<typename T>
class MyContainer {
public:
    using value_type = T;
    using size_type = size_t;
};

❌ Don't

// 1. Don't separate template implementation
// WON'T LINK:
// header.h: template<typename T> class C { void f(); };
// source.cpp: template<typename T> void C<T>::f() { }

// 2. Don't assume T has specific operations
template<typename T>
class Bad {
    void sort() { /* assumes T is sortable */ }
};

Quick Reference

// Basic class template
template<typename T>
class MyClass { T data; };

// Multiple parameters
template<typename T, typename U>
class Pair { T first; U second; };

// Non-type parameter
template<typename T, size_t N>
class Array { T data[N]; };

// Default arguments
template<typename T = int>
class Default { };

// Member outside class
template<typename T>
void MyClass<T>::method() { }

// Full specialization
template<>
class MyClass<int> { };

// Partial specialization
template<typename T>
class MyClass<T*> { };

Compile & Run

g++ -std=c++17 -Wall examples.cpp -o examples && ./examples
README - C++ Tutorial | DeepML