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