C++ Class Templates — Complete Guide
Introduction: Int stack, double stack—copy/paste forever?
Stop duplicating Stack for every type
You wrote IntStack, DoubleStack, same logic—only T differs. Class templates are the cookie cutter: one pattern, many baked shapes. You write Stack
// g++ -std=c++17 -o stack_tpl stack_tpl.cpp && ./stack_tpl
#include <vector>
#include <iostream>
#include <string>
#include <stdexcept>
// 실행 예제
template <typename T>
class Stack {
std::vector<T> data;
public:
void push(const T& value) { data.push_back(value); }
T pop() {
if (empty()) throw std::logic_error("Stack is empty");
T value = data.back();
data.pop_back();
return value;
}
bool empty() const { return data.empty(); }
size_t size() const { return data.size(); }
};
int main() {
Stack<int> intStack;
intStack.push(10);
intStack.push(20);
std::cout << intStack.pop() << "\n";
Stack<std::string> strStack;
strStack.push("hello");
strStack.push("world");
std::cout << strStack.pop() << "\n";
return 0;
}
Unlike function templates, you usually name the template arguments explicitly: Stack
flowchart TB
subgraph template[Template definition]
T["template \nclass Stack"]
end
subgraph instances[Instantiated types]
I1[Stack]
I2[Stack]
I3[Stack]
end
T -->|T=int| I1
T -->|T=double| I2
T -->|T=std::string| I3
Table of contents
- Basic syntax
- Member definitions outside class
- Partial specialization
- Template aliases
- Practical generic containers
- Complete Stack/Array/traits
- Common errors
- Best practices
- Production patterns
1. Basic syntax
template <typename T>
class Box {
T value;
public:
Box(const T& v) : value(v) {}
T get() const { return value; }
void set(const T& v) { value = v; }
};
Multiple parameters:
template <typename K, typename V>
class KeyValue { /* ... */ };
Default template arguments:
template <typename T, typename Container = std::vector<T>>
class Stack { /* ... */ };
2. Out-of-line member definitions
template <typename T>
void Container<T>::set(const T& v) {
value = v;
}
Templates: definitions typically stay in headers (same TU) to avoid link errors. Static data members:
template <typename T>
int Counter<T>::count = 0;
3. Partial specialization
Specialize for patterns (e.g. T*, T[], Pair<T,T>)—not in the primary template.
Note: A common pattern uses SmartPtr<T[]>-style partial specialization for array delete semantics—match your design to delete vs delete[].
4. Template aliases
template <typename T>
using Vec = std::vector<T>;
template <typename K, typename V>
using Map = std::unordered_map<K, V>;
template <typename T>
using StringMap = std::unordered_map<std::string, T>;
5. Practical generic containers
A fixed-capacity ring buffer is a good example of a class template that earns its genericity — the same logic works for int, std::string, or any movable type, and the capacity itself can be a non-type template parameter.
template <typename T, size_t Capacity>
class RingBuffer {
std::array<T, Capacity> data{};
size_t head_ = 0, count_ = 0;
public:
void push(const T& value) {
data[(head_ + count_) % Capacity] = value;
if (count_ < Capacity) ++count_;
else head_ = (head_ + 1) % Capacity; // overwrite oldest
}
T& front() { return data[head_]; }
size_t size() const { return count_; }
bool full() const { return count_ == Capacity; }
};
// RingBuffer<int, 4> for a fixed-size event log
// RingBuffer<std::string, 16> for a recent-commands history
Why a class template here: a function template can’t hold state between calls, and a non-template class would force one copy-pasted RingBuffer per (T, Capacity) pair.
6. Complete Stack/Array/traits example
Putting partial specialization, template aliases, and traits together in one self-contained example:
#include <vector>
#include <array>
#include <type_traits>
// Primary template: growable stack backed by std::vector
template <typename T, typename Container = std::vector<T>>
class Stack {
Container data;
public:
void push(const T& v) { data.push_back(v); }
void pop() { data.pop_back(); }
T& top() { return data.back(); }
bool empty() const { return data.empty(); }
};
// Partial specialization: fixed-size stack for a raw array backing store
template <typename T, size_t N>
class Stack<T, std::array<T, N>> {
std::array<T, N> data{};
size_t count_ = 0;
public:
void push(const T& v) { if (count_ < N) data[count_++] = v; }
void pop() { if (count_ > 0) --count_; }
T& top() { return data[count_ - 1]; }
bool empty() const { return count_ == 0; }
};
// Trait: detect whether a type is stack-like (has push/pop/top)
template <typename, typename = void>
struct is_stack_like : std::false_type {};
template <typename T>
struct is_stack_like<T, std::void_t<decltype(std::declval<T>().push(std::declval<typename T::value_type>()))>>
: std::true_type {};
int main() {
Stack<int> heapStack; // uses std::vector<int> backing
heapStack.push(1);
heapStack.push(2);
Stack<int, std::array<int, 8>> fixedStack; // uses partial specialization
fixedStack.push(1);
fixedStack.push(2);
return 0;
}
This mirrors how the standard library itself composes: std::stack is a container adapter over a configurable backing container, exactly like the Stack<T, Container> shown here.
7. Common errors
- Undefined reference to template member defined only in
.cpp→ put definition in header or explicit instantiation - Dependent names →
typename/templatekeyword >>in nested templates → fine in C++11+ (was> >in C++03)- CTAD fails for default-constructed
Box bwhenTcan’t be deduced → write Boxb
8. Best practices
- Prefer typename for type parameters
- static_assert constraints
- using for
value_type, iterators inside containers - if constexpr (C++17) for type-specific branches
9. Production patterns
- CRTP for static polymorphism
- Policy-based design (container type as template parameter)
- Explicit instantiation in
.cppfor selected types to reduce compile time:
template class Stack<int>;
template class Stack<std::string>;
Related posts
Keywords
C++ class template, template class, partial specialization, generic container, template alias, type traits, CRTP, policy design
Summary
| Topic | Detail |
|---|---|
| Syntax | template <typename T> class C { }; |
| Use | C<int> obj; (CTAD exceptions in C++17) |
| Members | Out-of-line definitions need template<typename T> and C<T>:: |
| Partial spec | Pattern-based specializations |
| Aliases | using Alias = Template<T>; |
| One-line summary: One class template replaces many copy-pasted classes; specialize patterns; keep definitions visible to the compiler. | |
| Next: Variadic templates #9-3 |
FAQ
When is this useful?
A. Building reusable containers, wrappers, and type-safe APIs—core of STL-style design.
Read first?
A. Template basics, series index.
Related posts
Related Articles (Internal Links)
Other articles related to this topic.
- C++ 템플릿 입문 | template
와 템플릿 컴파일 에러 해결법 - C++ 템플릿 특수화 완벽 가이드 | 완전·부분 특수화, 문제 시나리오, 프로덕션 패턴
- C++ 가변 인자 템플릿 | Variadic Templates와 Fold Expression
Keywords Covered in This Article (Related Search Terms)
This article covers C++, Class Template, Partial Specialization, template, using alias, CRTP.