C++ Templates: Beginner’s Guide to Generic Programming

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:

FeaturesDescription
Type InferenceCompiler automatically infers
Explicit designationmax<int>(3, 5)
OverloadingUsed with regular functions
InlineInline 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:

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.


  • 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 |