Template Class
C++ class templates define a generic class that accepts types as parameters. Containers (arrays, stacks, queues, etc.) and data structures can be implemented in a type-independent way, eliminating the need to hand-write separate int, double, and std::string versions. The standard library's std::vector and std::stack are also implemented as class templates.
Syntax
// ========================================
// Basic class template syntax
// ========================================
// Declare type parameter T with the template keyword
template <typename T>
class MyContainer {
private:
T data; // Member variable of type T
public:
// T can also be used in the constructor
MyContainer(T value) : data(value) {}
// T can be used as the return type and parameter type of methods
T get() const { return data; }
void set(T value) { data = value; }
};
// Instantiation: specify the concrete type inside <>
MyContainer<int> intBox(42); // Instantiated with T = int
MyContainer<std::string> strBox("hello"); // Instantiated with T = std::string
// ----------------------------------------
// Class template with multiple type parameters
// ----------------------------------------
template <typename K, typename V>
class Pair {
public:
K key;
V value;
Pair(K k, V v) : key(k), value(v) {}
};
Pair<std::string, int> p("score", 100);
// ----------------------------------------
// Default type argument (C++11 and later)
// ----------------------------------------
template <typename T = int>
class DefaultContainer {
T data;
};
DefaultContainer<> dc; // Treated as T = int
Syntax Reference
| Syntax / Concept | Description |
|---|---|
| template <typename T> class Name { }; | Declares a class template with type parameter T. Using class instead of typename is equivalent. |
| ClassName<Type> | Instantiates the class template with a specific type, such as <int> or <std::string>. |
| Multiple type parameters | Multiple type parameters can be declared as in template <typename K, typename V>. Useful for implementing pairs or map-like structures with separate key and value types. |
| Default type argument | A default type can be specified as in template <typename T = int>. The default type is used when <> is left empty. |
| Full specialization of a class template | template<> class Name<SpecificType> { }; provides a type-specific implementation. The specialization takes priority over the generic version. |
| Partial specialization | A different implementation can be defined for a subset of patterns, such as pointer types: template <typename T> class Name<T*> { };. |
| Non-type template parameter | Values can be used as parameters: template <typename T, int N>. Commonly used for fixed-size array containers. |
Sample Code
jujutsu_stack.cpp
// ========================================
// jujutsu_stack.cpp — class template sample
// Uses Jujutsu Kaisen characters to demonstrate
// implementing and using a generic stack container
// ========================================
#include <iostream>
#include <string>
#include <stdexcept>
// ========================================
// Class template: Stack<T>
// A fixed-size stack that can hold any type T
// Non-type template parameter N sets the maximum size
// ========================================
template <typename T, int N = 10>
class Stack {
private:
T items[N]; // Array of type T (fixed size N)
int top; // Index of the next push position
public:
// Constructor: initializes the stack as empty
Stack() : top(0) {}
// push(): pushes a value onto the top of the stack
void push(const T& item) {
if (top >= N) {
throw std::overflow_error("Stack is full.");
}
items[top++] = item;
}
// pop(): removes and returns the top value
T pop() {
if (top <= 0) {
throw std::underflow_error("Stack is empty.");
}
return items[--top];
}
// peek(): returns the top value without removing it
const T& peek() const {
if (top <= 0) {
throw std::underflow_error("Stack is empty.");
}
return items[top - 1];
}
// isEmpty(): returns true if the stack is empty
bool isEmpty() const { return top == 0; }
// size(): returns the current number of elements
int size() const { return top; }
};
// ========================================
// Class template: Pair<K, V>
// Holds two values of different types together
// ========================================
template <typename K, typename V>
class Pair {
public:
K key;
V value;
Pair(const K& k, const V& v) : key(k), value(v) {}
void display() const {
std::cout << key << ": " << value << std::endl;
}
};
// ========================================
// Full specialization for std::string: Stack<std::string, N>
// Adds a printAll() method only for the string stack
// ========================================
template <int N>
class Stack<std::string, N> {
private:
std::string items[N];
int top;
public:
Stack() : top(0) {}
void push(const std::string& item) {
if (top >= N) {
throw std::overflow_error("Stack is full.");
}
items[top++] = item;
}
std::string pop() {
if (top <= 0) {
throw std::underflow_error("Stack is empty.");
}
return items[--top];
}
bool isEmpty() const { return top == 0; }
int size() const { return top; }
// Additional method exclusive to the string specialization
// Prints all elements from bottom to top
void printAll() const {
std::cout << "--- Stack contents (bottom to top) ---" << std::endl;
for (int i = 0; i < top; i++) {
std::cout << " [" << i << "] " << items[i] << std::endl;
}
}
};
int main() {
// ----------------------------------------
// Stack<int> — manage technique power (int) on the stack
// Uses default N = 10
// ----------------------------------------
std::cout << "=== Cursed technique power stack (int) ===" << std::endl;
Stack<int> powerStack;
powerStack.push(3800); // Itadori Yuji's Black Flash power
powerStack.push(2900); // Fushiguro Megumi's shikigami power
powerStack.push(4200); // Gojo Satoru's Infinity technique power
powerStack.push(3100); // Nanami Kento's Ratio Technique power
std::cout << "Elements pushed: " << powerStack.size() << std::endl;
std::cout << "Top value (Nanami Kento): " << powerStack.peek() << std::endl;
std::cout << "Pop order (last in, first out):" << std::endl;
while (!powerStack.isEmpty()) {
std::cout << " " << powerStack.pop() << std::endl;
}
std::cout << std::endl;
// ----------------------------------------
// Stack<double> — manage cursed energy remaining (double)
// ----------------------------------------
std::cout << "=== Cursed energy remaining stack (double) ===" << std::endl;
Stack<double, 5> curseStack; // Maximum 5 entries
curseStack.push(98.5); // Itadori Yuji's remaining energy (%)
curseStack.push(87.2); // Fushiguro Megumi's remaining energy
curseStack.push(100.0); // Gojo Satoru's remaining energy
std::cout << "Remaining energy (starting from Gojo Satoru):" << std::endl;
while (!curseStack.isEmpty()) {
std::cout << " " << curseStack.pop() << " %" << std::endl;
}
std::cout << std::endl;
// ----------------------------------------
// Stack<std::string> — uses the string specialization
// printAll() is available here
// ----------------------------------------
std::cout << "=== Sortie order stack (string specialization) ===" << std::endl;
Stack<std::string, 5> nameStack;
nameStack.push("Nanami Kento");
nameStack.push("Kugisaki Nobara");
nameStack.push("Fushiguro Megumi");
nameStack.push("Itadori Yuji");
nameStack.printAll(); // Method exclusive to the string specialization
std::cout << "Popping from the top:" << std::endl;
std::cout << " " << nameStack.pop() << " sorties first." << std::endl;
std::cout << " " << nameStack.pop() << " sorties next." << std::endl;
std::cout << std::endl;
// ----------------------------------------
// Pair<K, V> — holds two values as a pair
// ----------------------------------------
std::cout << "=== Character and grade pairs ===" << std::endl;
Pair<std::string, std::string> p1("Itadori Yuji", "Grade 1 (special case)");
Pair<std::string, std::string> p2("Gojo Satoru", "Special Grade");
Pair<std::string, int> p3("Kugisaki Nobara", 3); // K=string, V=int
Pair<std::string, double> p4("Nanami Kento", 1.5); // Between Grade 1 and Special Grade
p1.display();
p2.display();
std::cout << p3.key << ": Semi-Grade 1 (sorcerer rank " << p3.value << ")" << std::endl;
std::cout << p4.key << ": Grade 1 sorcerer (proficiency " << p4.value << ")" << std::endl;
return 0;
}
# Compile g++ -std=c++11 jujutsu_stack.cpp -o jujutsu_stack # Run ./jujutsu_stack === Cursed technique power stack (int) === Elements pushed: 4 Top value (Nanami Kento): 3100 Pop order (last in, first out): 3100 4200 2900 3800 === Cursed energy remaining stack (double) === Remaining energy (starting from Gojo Satoru): 100 % 87.2 % 98.5 % === Sortie order stack (string specialization) === --- Stack contents (bottom to top) --- [0] Nanami Kento [1] Kugisaki Nobara [2] Fushiguro Megumi [3] Itadori Yuji Popping from the top: Itadori Yuji sorties first. Fushiguro Megumi sorties next. === Character and grade pairs === Itadori Yuji: Grade 1 (special case) Gojo Satoru: Special Grade Kugisaki Nobara: Semi-Grade 1 (sorcerer rank 3) Nanami Kento: Grade 1 sorcerer (proficiency 1.5)
Common Mistakes
Putting member function definitions of a class template in a .cpp file causes linker errors. Because templates generate type-specific code at compile time, the compiler must be able to see the member function implementations when it processes the header file.
stack.h (NG pattern)
// NG: Member function definitions are in a .cpp file (causes linker errors)
// stack.h
template <typename T>
class Stack {
public:
void push(const T& item); // Only the declaration is in the header
T pop();
private:
T items[10];
int top = 0;
};
stack.cpp (NG pattern)
// NG: Definitions are in .cpp — callers that use Stack<int> cannot link
#include "stack.h"
template <typename T>
void Stack<T>::push(const T& item) {
items[top++] = item;
}
template <typename T>
T Stack<T>::pop() {
return items[--top];
}
The basic rule is to write both declarations and definitions of class template member functions in the header file.
stack_ok.h (OK pattern)
// OK: Write both declarations and definitions in the header
// stack_ok.h
template <typename T>
class Stack {
public:
void push(const T& item) {
items[top++] = item; // Definition written directly in the header
}
T pop() {
return items[--top];
}
bool isEmpty() const { return top == 0; }
private:
T items[10];
int top = 0;
};
g++ -std=c++11 main.cpp -o main ./main
If you need to keep the declaration and definition in separate files, a common approach is to use an extension such as .tpp or .ipp for the implementation file and include it at the end of the header with #include "stack.tpp".
Overview
Class templates are declared with template <typename T> to define a generic class that accepts types as parameters. Each time the template is instantiated — as Stack<int> or Stack<std::string> — the compiler automatically generates type-specific class code. This eliminates the need to duplicate int, double, and std::string versions by hand. Multiple type parameters are allowed, enabling Pair<K, V>-style designs with separate key and value types. Combining non-type template parameters (template <typename T, int N>) allows array sizes to be handled safely as compile-time constants. When a specific type needs different behavior, defining a full specialization (template <int N> class Stack<std::string, N> { };) gives it priority over the generic version. Class templates, alongside function templates, form the backbone of the STL (Standard Template Library) — std::vector, std::stack, and std::map are all implemented as class templates.
If you find any errors or copyright issues, please contact us.