C++: Template Basics
Last updated: 2026-08-26
In previous lessons, the functions and classes we wrote could only handle one type.
But what if you want to write a
maxfunction that works withint,double, andstring?Templates solve this problem — allowing functions and classes to support any type.
1. What Are Templates?
(1) 1.1 Templates in Real Life*
| Real-life Analogy | Programming Equivalent |
|---|---|
| A cake mold (can pour in different flavors of batter) | Template |
| Word mail merge (one template, fill in different recipients) | Template |
The essence of templates: Let the compiler automatically generate the appropriate version of a function or class based on the types used.
(2) 1.2 Why Do We Need Templates?
Without templates (repeated code):
#include <iostream>
// max for int
int max(int a, int b) {
return a > b ? a : b;
}
// max for double
double max(double a, double b) {
return a > b ? a : b;
}
// ... You'd have to write N versions!
With templates (code reuse):
#include <iostream>
// Function template
template <typename T>
T max(T a, T b) {
return a > b ? a : b;
}
int main() {
std::cout << max(3, 5) << std::endl; // Compiler auto-generates int version
std::cout << max(3.14, 2.72) << std::endl; // Compiler auto-generates double version
return 0;
}
2. Function Templates*
(1) 2.1 Basic Syntax*
template <typename TypeName>
ReturnType functionName(parameterList) {
// function body
}
💡 Key point: typename can also be written as class (they are equivalent), but typename is recommended (more intuitive).
▶ Example 1: Function Template (Difficulty ⭐)
#include <iostream>
// Function template
template <typename T>
T max(T a, T b) {
return a > b ? a : b;
}
int main() {
std::cout << "max(3, 5) = " << max(3, 5) << std::endl;
std::cout << "max(3.14, 2.72) = " << max(3.14, 2.72) << std::endl;
// You can also explicitly specify the type
std::cout << "max<int>(3, 5) = " << max<int>(3, 5) << std::endl;
return 0;
}
Output:
max(3, 5) = 5
max(3.14, 2.72) = 3.14
max<int>(3, 5) = 5
3. Class Templates*
(1) 3.1 Basic Syntax*
template <typename TypeName>
class ClassName {
// members
};
▶ Example 2: Class Template Pair (Difficulty ⭐⭐)
#include <iostream>
#include <string>
// Class template: store a pair of values
template <typename T1, typename T2>
class Pair {
private:
T1 first;
T2 second;
public:
Pair(const T1& a, const T2& b) : first(a), second(b) {}
void print() {
std::cout << "(" << first << ", " << second << ")" << std::endl;
}
};
int main() {
Pair<int, double> p1(3, 3.14);
Pair<std::string, int> p2("Alice", 20);
p1.print(); // (3, 3.14)
p2.print(); // (Alice, 20)
return 0;
}
Output:
(3, 3.14)
(Alice, 20)
💡 Key point: Class templates must have their types explicitly specified when used (unless the constructor can deduce them).
4. Template Declaration and Definition*
(1) 4.1 The Problem: Where Should Templates Go?*
If you put the template declaration in .h and the definition in .cpp, you'll get a linker error!
Reason: A template is not a real function/class — it's a "mold" that generates code at compile time.
- If the declaration is in
.hand the definition is in.cpp, the compiler doesn't know which type versions to generate when compiling.cpp. - Only at the point of use does the compiler know which versions to generate.
(2) 4.2 Solution: Put Declaration and Definition Together*
Recommended approach: Put both the template declaration and definition in the .h file.
// pair.h (header file)
#ifndef PAIR_H
#define PAIR_H
template <typename T1, typename T2>
class Pair {
private:
T1 first;
T2 second;
public:
Pair(const T1& a, const T2& b);
void print();
};
// Definition goes here too!
template <typename T1, typename T2>
Pair<T1, T2>::Pair(const T1& a, const T2& b) : first(a), second(b) {}
template <typename T1, typename T2>
void Pair<T1, T2>::print() {
std::cout << "(" << first << ", " << second << ")" << std::endl;
}
#endif
💡 Tip: This is the biggest difference between templates and ordinary functions/classes — templates typically have their declaration and definition together.
5. Practice: Simple Array Template*
▶ Example 3: Implementing the Array Class Template (Difficulty ⭐⭐⭐)
#include <iostream>
#include <cassert>
template <typename T>
class Array {
private:
T* data;
int size;
public:
Array(int sz) : size(sz) {
data = new T[size];
}
~Array() {
delete data;
}
// Copy constructor (deep copy)
Array(const Array& other) : size(other.size) {
data = new T[size];
for (int i = 0; i < size; i++) {
data[i] = other.data[i];
}
}
// Overload operator
T& operator(int index) {
assert(index >= 0 && index < size);
return data[index];
}
int getSize() const {
return size;
}
};
int main() {
Array<int> arr(5);
for (int i = 0; i < arr.getSize(); i++) {
arr[i] = (i + 1) * 10;
}
for (int i = 0; i < arr.getSize(); i++) {
std::cout << arr[i] << " ";
}
std::cout << std::endl;
return 0;
}
Output:
10 20 30 40 50
▶ Example 3: Function Template (Difficulty ⭐)
#include <iostream>
template<typename T>
T getMax(T a, T b) {
return (a > b) ? a : b;
}
int main() {
std::cout << "int max: " << getMax(3, 5) << std::endl;
std::cout << "double max: " << getMax(3.14, 2.71) << std::endl;
std::cout << "string max: " << getMax("apple", "banana") << std::endl;
return 0;
}
Output:
int max: 5
double max: 3.14
string max: banana
T.
Q: What's the difference between templates and function overloading? A:> - Function overloading: multiple functions with the same name but different parameter types (the compiler determines which to call at compile time) > - Function templates: one "mold" from which the compiler automatically generates the appropriate version based on the types used > > Recommendation: If the logic is exactly the same (only the type differs), use templates; if the logic differs, use overloading. Q: Can templates support all types? A: No! Templates require types to support certain operations. > > template
<typename T>> T add(T a, T b) { > return a + b; // Requires T to support operator+ > } > > // If T is a custom class without an overloaded operator+, it will cause a compilation error. > Q: When should I use templates? A: When the logic of your function/class is type-independent. > > Typical scenarios: > - Container classes (std::vector,std::array) > - Algorithms (std::sort,std::find) > - Utility functions (std::max,std::min,std::swap)
Q: What's the most important thing about template basics? A: Understand the core concepts first, then reinforce them through practical examples.
❓ FAQ
📖 Summary
- Function templates: generic programming, instantiated at compile time
- Template parameters declared with typename/class
- Class templates: generic abstractions like containers and smart pointers
- Template specialization: custom implementations for specific types
- Advantages of templates: code reuse, type safety, zero overhead
📝 Exercises
-
Basic (Difficulty ⭐): Write a function template
T min(T a, T b)that returns the smaller of two values. -
Intermediate (Difficulty ⭐⭐): Write a class template
BoxTwith a member of typeT, and member functionsvoid set(T val)andT get(). -
Challenge (Difficulty ⭐⭐⭐): Extend the
Arrayclass template above by adding: -
void push_back(const T& val)(dynamic expansion) -
void pop_back() -
Implement it with templates so it can store any type
6. 🚀 Next Steps*
Now that you've learned template basics, next we'll study STL Containers (lesson 34) — using the ready-made containers provided by the C++ standard library, so you don't have to reinvent the wheel!