Templates & STL: Generic Programming in C++
Templates are C++'s mechanism for generic programming, allowing you to write code that works with different types. The Standard Template Library (STL) is built on templates and provides a rich collection of containers, algorithms, and utilities.
Template Types
- Function Templates: Generic functions
- Class Templates: Generic classes
- Variable Templates: Generic variables (C++14)
- Alias Templates: Generic type aliases
Template Types
- Function Templates: Generic functions
- Class Templates: Generic classes
- Variable Templates: Generic variables (C++14)
- Alias Templates: Generic type aliases
STL Components
- Containers: Data structures (vector, map, etc.)
- Algorithms: Functions (sort, find, etc.)
- Iterators: Pointer-like objects
- Function Objects: Callable objects
// Basic function template
template<typename T>
T max(T a, T b) {
return (a > b) ? a : b;
}
// Usage with different types
int i = max(10, 20); // T = int
double d = max(3.14, 2.71); // T = double
std::string s = max(std::string("hello"), std::string("world"));
// Template argument deduction
template<typename T, typename U>
auto add(T a, U b) -> decltype(a + b) { // C++11
return a + b;
}
// C++14 and later - even simpler
template<typename T, typename U>
auto multiply(T a, U b) {
return a * b;
}
// Template specialization
template<typename T>
void print(T value) {
std::cout << value << std::endl;
}
// Specialization for strings
template<>
void print<std::string>(std::string value) {
std::cout << "String: " << value << std::endl;
}Template Design
- • Use meaningful template parameter names
- • Provide default template arguments when sensible
- • Consider template argument deduction
- • Use SFINAE or concepts for constraints
- • Separate interface from implementation
STL Usage
- • Prefer STL algorithms over hand-written loops
- • Choose appropriate container for your use case
- • Use iterators for generic code
- • Reserve capacity for vectors when size is known
- • Use emplace operations instead of insert
Function Templates
Basic Syntax
template<typename T>
T max(T a, T b) {
return (a > b) ? a : b;
}
// Usage
int i = max(10, 20); // T = int
double d = max(3.14, 2.71); // T = double
Template Argument Deduction
template<typename T, typename U>
auto add(T a, U b) -> decltype(a + b) {
return a + b;
}
// C++14 and later
template<typename T, typename U>
auto add(T a, U b) {
return a + b;
}
Specialization
// Primary template
template<typename T>
void print(T value) {
std::cout << value << std::endl;
}
// Specialization for strings
template<>
void print<std::string>(std::string value) {
std::cout << "String: " << value << std::endl;
}
Class Templates
Basic Class Template
template<typename T>
class Stack {
private:
std::vector<T> data;
public:
void push(const T& item) {
data.push_back(item);
}
T pop() {
if (empty()) {
throw std::runtime_error("Stack is empty");
}
T item = data.back();
data.pop_back();
return item;
}
bool empty() const {
return data.empty();
}
size_t size() const {
return data.size();
}
};
// Usage
Stack<int> intStack;
Stack<std::string> stringStack;
Template Parameters
template<
typename T, // Type parameter
size_t N = 10, // Non-type parameter with default
typename Allocator = std::allocator<T> // Type parameter with default
>
class FixedArray {
T data[N];
// ...
};
STL Components
Containers
- Sequence Containers: vector, deque, list, array, forward_list
- Associative Containers: set, map, multiset, multimap
- Unordered Containers: unordered_set, unordered_map, etc.
- Container Adaptors: stack, queue, priority_queue
Container Examples
// Vector - dynamic array
std::vector<int> vec{1, 2, 3, 4, 5};
// Map - key-value pairs
std::map<std::string, int> wordCount;
wordCount["hello"] = 5;
// Set - unique elements
std::set<int> uniqueNumbers{3, 1, 4, 1, 5, 9}; // {1, 3, 4, 5, 9}
// Unordered map - hash table
std::unordered_map<std::string, double> prices;
prices["apple"] = 1.50;
Iterators
Iterator Categories
- Input Iterator: Read-only, single-pass
- Output Iterator: Write-only, single-pass
- Forward Iterator: Read/write, multi-pass
- Bidirectional Iterator: Forward + backward
- Random Access Iterator: Bidirectional + jump
Iterator Usage
std::vector<int> vec{1, 2, 3, 4, 5};
// Iterator-based loop
for (auto it = vec.begin(); it != vec.end(); ++it) {
std::cout << *it << " ";
}
// Range-based for (C++11+)
for (const auto& value : vec) {
std::cout << value << " ";
}
// Iterator arithmetic
auto it = vec.begin();
std::advance(it, 2); // Move iterator 2 positions
auto distance = std::distance(vec.begin(), vec.end());
STL Algorithms
Algorithm Categories
- Non-modifying: find, count, search
- Modifying: copy, transform, replace
- Sorting: sort, partial_sort, nth_element
- Binary Search: lower_bound, upper_bound, binary_search
- Set Operations: set_union, set_intersection
Algorithm Examples
std::vector<int> vec{3, 1, 4, 1, 5, 9, 2, 6, 5};
// Sorting
std::sort(vec.begin(), vec.end());
// Finding elements
auto it = std::find(vec.begin(), vec.end(), 5);
if (it != vec.end()) {
std::cout << "Found 5 at position " << std::distance(vec.begin(), it);
}
// Transforming elements
std::vector<int> squares(vec.size());
std::transform(vec.begin(), vec.end(), squares.begin(),
[](int x) { return x * x; });
// Filtering
std::vector<int> evens;
std::copy_if(vec.begin(), vec.end(), std::back_inserter(evens),
[](int x) { return x % 2 == 0; });
// Reducing
int sum = std::accumulate(vec.begin(), vec.end(), 0);
Template Metaprogramming
SFINAE (Substitution Failure Is Not An Error)
#include <type_traits>
// Enable if T is integral
template<typename T>
typename std::enable_if<std::is_integral<T>::value, T>::type
processValue(T value) {
return value * 2;
}
// Enable if T is floating point
template<typename T>
typename std::enable_if<std::is_floating_point<T>::value, T>::type
processValue(T value) {
return value * 3.14;
}
Modern Approach with Concepts (C++20)
#include <concepts>
template<std::integral T>
T processValue(T value) {
return value * 2;
}
template<std::floating_point T>
T processValue(T value) {
return value * 3.14;
}
Type Traits
Standard Type Traits
#include <type_traits>
template<typename T>
void analyzeType() {
if constexpr (std::is_integral_v<T>) {
std::cout << "T is an integral type\n";
}
if constexpr (std::is_pointer_v<T>) {
std::cout << "T is a pointer type\n";
}
if constexpr (std::is_const_v<T>) {
std::cout << "T is const\n";
}
}
Custom Type Traits
// Check if a type has a specific member function
template<typename T, typename = void>
struct has_size : std::false_type {};
template<typename T>
struct has_size<T, std::void_t<decltype(std::declval<T>().size())>>
: std::true_type {};
template<typename T>
constexpr bool has_size_v = has_size<T>::value;
Perfect Forwarding
Universal References and Forwarding
template<typename T>
void wrapper(T&& arg) {
// Perfect forwarding preserves value category
actualFunction(std::forward<T>(arg));
}
// Variadic template with perfect forwarding
template<typename F, typename... Args>
auto call_function(F&& f, Args&&... args)
-> decltype(f(std::forward<Args>(args)...)) {
return f(std::forward<Args>(args)...);
}
Best Practices
Template Design
- Use meaningful template parameter names
- Provide default template arguments when sensible
- Consider template argument deduction
- Use SFINAE or concepts for constraints
- Separate interface from implementation
STL Usage
Match containers and algorithms to the workload
- Prefer STL algorithms over hand-written loops
- Choose appropriate container for your use case
- Use iterators for generic code
- Take advantage of range-based for loops
- Understand iterator invalidation rules
Performance Tips
- Reserve capacity for vectors when size is known
- Use emplace operations instead of insert
- Prefer algorithms that work in-place
- Choose unordered containers for hash-based lookups
- Use move semantics with containers
Modern C++ Enhancements
C++11
- Range-based for loops
- Auto type deduction
- Variadic templates
- Move semantics in containers
C++14
- Generic lambdas
- Variable templates
- Make functions for containers
C++17
- Structured bindings
- Parallel algorithms
- std::optional and std::variant
C++20
- Concepts
- Ranges library
- Coroutines
Templates and STL form the backbone of modern C++ programming, enabling efficient, generic, and reusable code.
Related concepts
Master C++ OOP concepts including inheritance, polymorphism, virtual functions, and modern object-oriented design principles with interactive examples.
Follow a C++ function from tokens to a simplified AST, then inspect exact source ranges, declaration references and types in a keyboard-accessible explorer.
Understand the complete C++ compilation pipeline from source code to object files. Learn preprocessing, parsing, code generation, and optimization stages.
Understand C++ pointers and references with step-by-step memory diagrams: addressing, dereferencing, aliasing, array arithmetic, and object lifetime.
C++ preprocessor visualized: macros, header guards, conditional compilation, and #include directives explained interactively.
Learn Resource Acquisition Is Initialization (RAII) - the cornerstone of C++ memory management. Understand automatic resource cleanup and exception safety.
