Templates & STL in C++

Master C++ templates and the Standard Template Library. Learn generic programming, template metaprogramming, and STL containers and algorithms.

Best viewed on desktop for optimal interactive experience

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

  1. Function Templates: Generic functions
  2. Class Templates: Generic classes
  3. Variable Templates: Generic variables (C++14)
  4. Alias Templates: Generic type aliases

Templates & STL Overview

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

Interactive Template & STL Explorer

// 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 argument deduction allows the compiler to automatically determine template parameters from function arguments.

Best Practices & Performance Tips

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

  1. Sequence Containers: vector, deque, list, array, forward_list
  2. Associative Containers: set, map, multiset, multimap
  3. Unordered Containers: unordered_set, unordered_map, etc.
  4. 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

  1. Input Iterator: Read-only, single-pass
  2. Output Iterator: Write-only, single-pass
  3. Forward Iterator: Read/write, multi-pass
  4. Bidirectional Iterator: Forward + backward
  5. 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

  1. Non-modifying: find, count, search
  2. Modifying: copy, transform, replace
  3. Sorting: sort, partial_sort, nth_element
  4. Binary Search: lower_bound, upper_bound, binary_search
  5. 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

  1. Use meaningful template parameter names
  2. Provide default template arguments when sensible
  3. Consider template argument deduction
  4. Use SFINAE or concepts for constraints
  5. Separate interface from implementation

STL Usage

  1. Prefer STL algorithms over hand-written loops
  2. Choose appropriate container for your use case
  3. Use iterators for generic code
  4. Take advantage of range-based for loops
  5. Understand iterator invalidation rules

Performance Tips

  1. Reserve capacity for vectors when size is known
  2. Use emplace operations instead of insert
  3. Prefer algorithms that work in-place
  4. Choose unordered containers for hash-based lookups
  5. 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.

If you found this explanation helpful, consider sharing it with others.

Mastodon