C++ Templates: Beginner’s Guide to Generic Programming
이 글의 핵심
Hands-on introduction to C++ templates—syntax, instantiation, and patterns you will use with the STL every day.
What is a template?
Template is a C++ generic programming technique that writes type-independent code.Code is generated for each type at compile time.
Why do you need it?:
- Code reuse: Removal of duplication by type
- Type safety: Compile-time type checking
- Performance: No runtime overhead
- Flexibility: Supports various types
// ❌ Duplicate code by type
int maxInt(int a, int b) { return (a > b) ?a:b;}
double maxDouble(double a, double b) { return (a > b) ?a : b;}
char maxChar(char a, char b) { return (a > b) ?a:b;}
// ✅ Template: reuse
template <typename T>
T max(T a, T b) {
return (a > b) ?a:b;
}
max(3, 5);// int
max(3.5, 2.1);// double
max('a', 'z');// char
How the template works:
“mermaid flowchart LR A[template definition] —> B[compiler] B —>|int call|C[create int version] B —>|double call|D[Create double version] B —>|string call|E[create string version] C —> F[executable file] D —> F E —> F
Template Instantiation:
```cpp
// template definition
template <typename T>
T max(T a, T b) {
return (a > b) ?a:b;
}
// call
max(3, 5);// create int version
max(3.5, 2.1);// create double version
// internal workings (conceptual)
// int max(int a, int b) { return (a > b) ?a:b;}
// double max(double a, double b) { return (a > b) ?a:b;}
Function template
template <typename T>
T max(T a, T b) {
return (a > b) ?a:b;
}
int main() {
cout << max(3, 5) << endl;// int
cout << max(3.5, 2.1) << endl;// double
cout << max('a', 'z') << endl;// char
}
Function Template Features:
| Features | Description |
|---|---|
| Type Inference | Compiler automatically infers |
| Explicit designation | max<int>(3, 5) |
| Overloading | Used with regular functions |
| Inline | Inline optimization possible |
// type inference
max(3, 5);// T = int
// explicit designation
max<double>(3, 5);// T = double
// overloading
template <typename T>
T max(T a, T b) { return (a > b) ?a:b;}
int max(int a, int b) { return (a > b) ?a:b;} // Generic function first
class template
template <typename T>
class Box {
private:
T value;
public:
Box(T v) : value(v) {}
T getValue() { return value;}
void setValue(T v) { value = v;}
};
int main() {
Box<int> intBox(10);
Box<string> strBox("Hello");
cout << intBox.getValue() << endl;
cout << strBox.getValue() << endl;
}
Multiple type parameters
template <typename T, typename U>
class Pair {
public:
T first;
U second;
Pair(T f, U s) : first(f), second(s) {}
};
int main() {
Pair<int, string> p(1, "one");
cout << p.first << ": " << p.second << endl;
}
Practical example
Example 1: Generic Stack
#include <iostream>
#include <vector>
using namespace std;
template <typename T>
class Stack {
private:
vector<T> data;
public:
void push(const T& value) {
data.push_back(value);
}
void pop() {
if (!empty()) {
data.pop_back();
}
}
T& top() {
return data.back();
}
bool empty() const {
return data.empty();
}
size_t size() const {
return data.size();
}
};
int main() {
Stack<int> intStack;
intStack. push(1);
intStack. push(2);
cout << intStack.top() << endl;// 2
Stack<string> strStack;
strStack.push("Hello");
strStack.push("World");
cout << strStack.top() << endl;// World
return 0;
}
Description: A generic stack that can store any type.
Example 2: Generic array utility
#include <iostream>
#include <vector>
using namespace std;
template <typename T>
void printArray(const vector<T>& arr, const string& label) {
cout << label << ": ";
for (const T& item : arr) {
cout << item << " ";
}
cout << endl;
}
template <typename T>
T findMax(const vector<T>& arr) {
T maxVal = arr[0];
for (const T& item : arr) {
if (item > maxVal) {
maxVal = item;
}
}
return maxVal;
}
template <typename T>
vector<T> filter(const vector<T>& arr, bool (*pred)(T)) {
vector<T> result;
for (const T& item : arr) {
if (pred(item)) {
result.push_back(item);
}
}
return result;
}
bool isEven(int x) { return x % 2 == 0;}
int main() {
vector<int> numbers = {1, 2, 3, 4, 5};
printArray(numbers, "numbers");
cout << "Maximum value: " << findMax(numbers) << endl;
vector<int> evens = filter(numbers, isEven);
printArray(evens, "even number");
vector<string> words = {"apple", "banana", "cherry"};
printArray(words, "words");
return 0;
}
Description: Templates allow you to create type-independent utility functions.
Example 3: Generic Singleton
#include <iostream>
#include <memory>
using namespace std;
template <typename T>
class Singleton {
private:
static unique_ptr<T> instance;
protected:
Singleton() {}
public:
Singleton(const Singleton&) = delete;
Singleton& operator=(const Singleton&) = delete;
static T& getInstance() {
if (!instance) {
instance = make_unique<T>();
}
return *instance;
}
};
template <typename T>
unique_ptr<T> Singleton<T>::instance = nullptr;
class Config : public Singleton<Config> {
private:
int value;
public:
Config() : value(0) {}
void setValue(int v) { value = v;}
int getValue() { return value;}
};
int main() {
Config::getInstance().setValue(100);
cout << Config::getInstance().getValue() << endl;// 100
return 0;
}
Description: Implements a reusable singleton pattern as a template.
Frequently occurring problems
Issue 1: Separating template definitions into cpp files
Symptom: Link error occurs
Cause: Templates are instantiated at compile time and therefore require definition in header.
Solution:
// ❌ Wrong way
//header.h
template <typename T>
T max(T a, T b);
//source.cpp
template <typename T>
T max(T a, T b) {
return (a > b) ?a:b;
}
// Link error!
// ✅ Correct way (defined in header)
//header.h
template <typename T>
T max(T a, T b) {
return (a > b) ?a:b;
}
Problem 2: typename vs class
Symptom: Confusion about whether to use typename or class
Cause: Both are possible, but have slightly different meanings
Solution:
// both possible
template <typename T>
template <class T>
// Recommended: use typename (clearer)
template <typename T>
// If typename is required
template <typename T>
void func() {
typename T::value_type v;// nested type of T
}
Issue 3: Template specialization
Symptom: Different implementations required for specific types
Cause: General templates cannot handle all types
Solution:
// general template
template <typename T>
class Printer {
public:
void print(T value) {
cout << value << endl;
}
};
// bool specialization
template <>
class Printer<bool> {
public:
void print(bool value) {
cout << (value ? "true" : "false") << endl;
}
};
int main() {
Printer<int> intPrinter;
intPrinter.print(10);// 10
Printer<bool> boolPrinter;
boolPrinter.print(true);//true
}
Practice pattern
Pattern 1: Type-safe wrapper
template <typename T>
class Handle {
T* ptr_;
public:
explicit Handle(T* ptr) : ptr_(ptr) {}
~Handle() {
delete ptr_;
}
T* get() const { return ptr_;}
T& operator*() const { return *ptr_;}
T* operator->() const { return ptr_;}
Handle(const Handle&) = delete;
Handle& operator=(const Handle&) = delete;
};
// use
Handle<int> handle(new int(42));
std::cout << *handle << '\n';
Pattern 2: General purpose algorithm
template <typename T>
void swap(T& a, T& b) {
T temp = std::move(a);
a = std::move(b);
b = std::move(temp);
}
template <typename T>
T clamp(T value, T min, T max) {
if (value < min) return min;
if (value > max) return max;
return value;
}
// use
int x = 5, y = 10;
swap(x, y);// x=10, y=5
int clamped = clamp(15, 0, 10);// 10
Pattern 3: Container Adapter
template <typename T, typename Container = std::vector<T>>
class Stack {
Container data_;
public:
void push(const T& value) {
data_.push_back(value);
}
void pop() {
if (!empty()) {
data_.pop_back();
}
}
T& top() {
return data_.back();
}
bool empty() const {
return data_.empty();
}
};
// use
Stack<int> stack;// based on std::vector
Stack<int, std::deque<int>> dequeStack;// based on std::deque
FAQ
Q1: When do I use templates?
A: Used when writing type-independent code.All STL containers are templates.
template <typename T>
T max(T a, T b) {
return (a > b) ?a:b;
}
Q2: What is the difference between template and macro?
A:
- Template: type safety, compile time checking
- Macro: Simple text replacement, risky
// ❌ Macro: not type safe
#define MAX(a, b) ((a) > (b) ? (a) : (b))
// ✅ Template: type safe
template <typename T>
T max(T a, T b) { return (a > b) ?a:b;}
Q3: Do templates affect performance?
A: No, the code is generated at compile time so there is no runtime overhead.
// Template: generated at compile time
max(3, 5);// create int max(int, int)
// no runtime cost
Q4: What are variable argument templates?
A: Supported starting from C++11.
template <typename...Args>
void print(Args... args) {
(std::cout << ... << args) << '\n';
}
print(1, 2, 3, "hello");
Q5: Template compilation error is too long!
A: Using C++20 Concepts simplifies error messages.
// ❌ Long error message
template <typename T>
T add(T a, T b) { return a + b;}
// ✅ Concepts: concise errors
template <typename T>
requires std::is_arithmetic_v<T>
T add(T a, T b) { return a + b;}
Q6: What about template debugging?
A:
static_assert: Compile-time check- Explicit instantiation: Error checking -Concepts: specify constraints
template <typename T>
T add(T a, T b) {
static_assert(std::is_arithmetic_v<T>, "Numeric types only");
return a + b;
}
Q7: Can I separate the template definition into a cpp file?
A: Impossible.Templates must be defined in the header.
// ❌ Link error
//header.h
template <typename T>
T max(T a, T b);
//source.cpp
template <typename T>
T max(T a, T b) { return (a > b) ?a:b;}
// ✅ Defined in header
//header.h
template <typename T>
T max(T a, T b) { return (a > b) ?a:b;}
Q8: What are the template learning resources?
A:
- “C++ Templates: The Complete Guide” by David Vandevoorde
- “Effective Modern C++” by Scott Meyers
- cppreference.com - Templates
Related articles: template-specialization, variadic-templates, SFINAE.
One-Line Summary: Templates are a C++ generic programming technique for writing type-independent code.
Good article to read together (internal link)
Here’s another article related to this topic.
- C++ variable argument template |“The Complete Guide to Variadic Templates”
- C++ constexpr if |“Compile-time branching” guide
- C++ SFINAE |“Substitution Failure Is Not An Error” guide
Practical tips
These are tips that can be applied right away in practice.
Debugging tips
- If you run into a problem, check the compiler warnings first.
- Reproduce the problem with a simple test case
Performance Tips
- Don’t optimize without profiling
- Set measurable indicators first
Code review tips
- Check in advance for areas that are frequently pointed out in code reviews.
- Follow your team’s coding conventions
Practical checklist
This is what you need to check when applying this concept in practice.
Before writing code
- Is this technique the best way to solve the current problem?
- Can team members understand and maintain this code?
- Does it meet the performance requirements?
Writing code
- Have you resolved all compiler warnings?
- Have you considered edge cases?
- Is error handling appropriate?
When reviewing code
- Is the intention of the code clear?
- Are there enough test cases?
- Is it documented?
Use this checklist to reduce mistakes and improve code quality.
Keywords covered in this article (related search terms)
This article will be helpful if you search for C++, template, template, generic, type, etc.
Related articles
- C++ variable argument template |
- [C++ auto type inference |Leaving complex types to the compiler
- C++ CTAD |
- C++20 Concepts Complete Guide |A new era of template constraints
- C++ constexpr if |