Standard Template Library (STL)

Master the powerful STL containers, algorithms, and utilities

Introduction to STL

The Standard Template Library (STL) is a collection of template classes and functions that provide common data structures and algorithms.

STL Components:

Containers

Data structures like vector, list, map, set, etc.

Iterators

Objects that point to elements in containers

Algorithms

Functions for searching, sorting, and manipulating data

Function Objects

Objects that can be called like functions

Benefits of STL: Reusable code, high performance, type safety, and extensive functionality tested by millions of developers.

Containers

STL containers are template classes that store collections of objects. They provide a consistent interface for managing data and are designed to work seamlessly with STL algorithms.

Container Categories:

Sequence Containers

Store elements in a linear sequence: vector, deque, list, array, forward_list

Associative Containers

Store elements in sorted order: set, multiset, map, multimap

Unordered Associative

Hash-based containers: unordered_set, unordered_multiset, unordered_map, unordered_multimap

Container Adaptors

Provide specialized interfaces: stack, queue, priority_queue

Performance Comparison

Container Access Insert/Delete Front Insert/Delete Back Insert/Delete Middle
vector O(1) O(n) O(1) O(n)
deque O(1) O(1) O(1) O(n)
list O(n) O(1) O(1) O(1)
set/map O(log n) O(log n) O(log n) O(log n)
containers_example.cpp
#include <iostream>
#include <vector>
#include <list>
#include <deque>
#include <set>
#include <map>
#include <unordered_map>
#include <unordered_set>
#include <stack>
#include <queue>
#include <array>
#include <forward_list>
#include <string>
#include <algorithm>
using namespace std;

int main() {
    // Vector - Dynamic array (most commonly used)
    cout << "=== Vector - Dynamic Array ===" << endl;
    vector<int> vec = {1, 2, 3, 4, 5};
    vec.push_back(6);
    vec.insert(vec.begin() + 2, 10); // Insert 10 at index 2
    vec.emplace_back(7); // Construct element in place
    
    cout << "Vector elements: ";
    for (const auto& elem : vec) {
        cout << elem << " ";
    }
    cout << endl;
    cout << "Size: " << vec.size() << ", Capacity: " << vec.capacity() << endl;
    cout << "Front: " << vec.front() << ", Back: " << vec.back() << endl;
    
    // Vector operations
    vec.reserve(20); // Reserve capacity
    cout << "After reserve(20), capacity: " << vec.capacity() << endl;
    vec.shrink_to_fit(); // Reduce capacity to size
    cout << "After shrink_to_fit, capacity: " << vec.capacity() << endl;
    
    // Array - Fixed-size container
    cout << "\n=== Array - Fixed Size ===" << endl;
    array<int, 5> arr = {10, 20, 30, 40, 50};
    cout << "Array size: " << arr.size() << endl;
    cout << "Array elements: ";
    for (const auto& elem : arr) {
        cout << elem << " ";
    }
    cout << endl;
    
    // List - Doubly linked list
    cout << "\n=== List - Doubly Linked List ===" << endl;
    list<string> lst = {"apple", "banana", "cherry"};
    lst.push_front("orange");
    lst.push_back("grape");
    lst.emplace_front("mango");
    lst.sort();
    
    cout << "List elements: ";
    for (const auto& elem : lst) {
        cout << elem << " ";
    }
    cout << endl;
    
    // List unique operations
    list<int> numbers = {1, 2, 2, 3, 3, 3, 4, 5, 5};
    cout << "Before unique: ";
    for (const auto& n : numbers) cout << n << " ";
    cout << endl;
    
    numbers.unique(); // Remove consecutive duplicates
    cout << "After unique: ";
    for (const auto& n : numbers) cout << n << " ";
    cout << endl;
    
    // Forward List - Singly linked list
    cout << "\n=== Forward List - Singly Linked ===" << endl;
    forward_list<int> flist = {1, 2, 3, 4, 5};
    flist.push_front(0);
    flist.emplace_front(-1);
    
    cout << "Forward list: ";
    for (const auto& elem : flist) {
        cout << elem << " ";
    }
    cout << endl;
    
    // Deque - Double-ended queue
    cout << "\n=== Deque - Double-ended Queue ===" << endl;
    deque<int> dq = {3, 4, 5};
    dq.push_front(2);
    dq.push_back(6);
    dq.push_front(1);
    dq.emplace_front(0);
    dq.emplace_back(7);
    
    cout << "Deque elements: ";
    for (const auto& elem : dq) {
        cout << elem << " ";
    }
    cout << endl;
    cout << "Front: " << dq.front() << ", Back: " << dq.back() << endl;
    
    // Set - Ordered unique elements
    cout << "\n=== Set - Ordered Unique Elements ===" << endl;
    set<int> s = {5, 2, 8, 2, 1, 9, 1}; // Duplicates automatically removed
    s.insert(3);
    s.insert(7);
    s.emplace(4);
    
    cout << "Set elements: ";
    for (const auto& elem : s) {
        cout << elem << " ";
    }
    cout << endl;
    
    // Set operations
    auto it = s.find(5);
    if (it != s.end()) {
        cout << "Found 5 in set" << endl;
    }
    cout << "Count of 5: " << s.count(5) << endl;
    cout << "Lower bound of 5: " << *s.lower_bound(5) << endl;
    cout << "Upper bound of 5: " << *s.upper_bound(5) << endl;
    
    // Multiset - Ordered elements with duplicates
    cout << "\n=== Multiset - Allows Duplicates ===" << endl;
    multiset<int> ms = {5, 2, 8, 2, 1, 9, 1, 5, 5};
    cout << "Multiset elements: ";
    for (const auto& elem : ms) {
        cout << elem << " ";
    }
    cout << endl;
    cout << "Count of 5: " << ms.count(5) << endl;
    
    // Map - Key-value pairs
    cout << "\n=== Map - Key-Value Pairs ===" << endl;
    map<string, int> ages;
    ages["Alice"] = 25;
    ages["Bob"] = 30;
    ages["Charlie"] = 35;
    ages.insert({"David", 28});
    ages.emplace("Eve", 32);
    
    cout << "Map elements:" << endl;
    for (const auto& [name, age] : ages) {
        cout << name << ": " << age << " years old" << endl;
    }
    
    // Map operations
    if (ages.find("Alice") != ages.end()) {
        cout << "Alice found with age: " << ages["Alice"] << endl;
    }
    
    // Unordered Set - Hash-based unique elements
    cout << "\n=== Unordered Set - Hash-based Unique ===" << endl;
    unordered_set<string> words = {"hello", "world", "cpp", "stl"};
    words.insert("programming");
    words.emplace("modern");
    
    cout << "Unordered set elements: ";
    for (const auto& word : words) {
        cout << word << " ";
    }
    cout << endl;
    cout << "Bucket count: " << words.bucket_count() << endl;
    cout << "Load factor: " << words.load_factor() << endl;
    
    // Unordered Map - Hash table
    cout << "\n=== Unordered Map - Hash Table ===" << endl;
    unordered_map<string, double> prices;
    prices["apple"] = 1.50;
    prices["banana"] = 0.75;
    prices["orange"] = 2.00;
    prices.emplace("grape", 3.25);
    
    cout << "Prices:" << endl;
    for (const auto& [item, price] : prices) {
        cout << item << ": $" << price << endl;
    }
    
    cout << "Average access time: O(1)" << endl;
    cout << "Bucket count: " << prices.bucket_count() << endl;
    
    // Stack - LIFO container adaptor
    cout << "\n=== Stack - LIFO Container ===" << endl;
    stack<int> stk;
    stk.push(10);
    stk.push(20);
    stk.push(30);
    stk.emplace(40);
    
    cout << "Stack size: " << stk.size() << endl;
    cout << "Stack elements (popping): ";
    while (!stk.empty()) {
        cout << stk.top() << " ";
        stk.pop();
    }
    cout << endl;
    
    // Queue - FIFO container adaptor
    cout << "\n=== Queue - FIFO Container ===" << endl;
    queue<string> q;
    q.push("first");
    q.push("second");
    q.push("third");
    q.emplace("fourth");
    
    cout << "Queue size: " << q.size() << endl;
    cout << "Front: " << q.front() << ", Back: " << q.back() << endl;
    cout << "Queue elements (dequeuing): ";
    while (!q.empty()) {
        cout << q.front() << " ";
        q.pop();
    }
    cout << endl;
    
    // Priority Queue - Heap-based container
    cout << "\n=== Priority Queue - Max Heap ===" << endl;
    priority_queue<int> pq;
    pq.push(30);
    pq.push(10);
    pq.push(50);
    pq.push(20);
    pq.emplace(45);
    
    cout << "Priority queue size: " << pq.size() << endl;
    cout << "Priority queue elements (max heap): ";
    while (!pq.empty()) {
        cout << pq.top() << " ";
        pq.pop();
    }
    cout << endl;
    
    // Min heap priority queue
    priority_queue<int, vector<int>, greater<int>> min_pq;
    min_pq.push(30);
    min_pq.push(10);
    min_pq.push(50);
    min_pq.push(20);
    
    cout << "Min heap priority queue: ";
    while (!min_pq.empty()) {
        cout << min_pq.top() << " ";
        min_pq.pop();
    }
    cout << endl;
    
    return 0;
}

Container Selection Tips:

  • Use vector as default choice for sequential data
  • Use deque when you need efficient front/back operations
  • Use list for frequent insertions/deletions in the middle
  • Use set/map for sorted, unique data
  • Use unordered_set/unordered_map for fast lookups

Iterators

Iterators are objects that point to elements in containers and provide a way to traverse through them. They act as a bridge between containers and algorithms, enabling generic programming.

Iterator Categories:

Input Iterator

Read-only, forward movement only. Used for reading from input streams.

Operations: ++, *, ==, !=

Output Iterator

Write-only, forward movement only. Used for writing to output streams.

Operations: ++, *

Forward Iterator

Read/write, forward movement only. Used by forward_list, unordered containers.

Operations: ++, *, ==, !=, ->

Bidirectional Iterator

Read/write, forward and backward movement. Used by list, set, map.

Operations: ++, --, *, ==, !=, ->

Random Access Iterator

Read/write, jump to any position. Used by vector, deque, array.

Operations: ++, --, +, -, [], *, ==, !=, <, >, <=, >=
iterators_example.cpp
#include <iostream>
#include <vector>
#include <list>
#include <set>
#include <map>
#include <iterator>
#include <algorithm>
#include <numeric>
#include <sstream>
using namespace std;

int main() {
    // Basic iterator usage with vector (Random Access Iterator)
    cout << "=== Random Access Iterator (vector) ===" << endl;
    vector<int> vec = {10, 20, 30, 40, 50};
    
    // Forward iteration
    cout << "Forward iteration: ";
    for (auto it = vec.begin(); it != vec.end(); ++it) {
        cout << *it << " ";
    }
    cout << endl;
    
    // Reverse iteration
    cout << "Reverse iteration: ";
    for (auto it = vec.rbegin(); it != vec.rend(); ++it) {
        cout << *it << " ";
    }
    cout << endl;
    
    // Constant iterators
    cout << "Using const_iterator: ";
    for (auto it = vec.cbegin(); it != vec.cend(); ++it) {
        cout << *it << " ";
        // *it = 100; // Error! Cannot modify through const_iterator
    }
    cout << endl;
    
    // Iterator arithmetic (random access iterators)
    cout << "\n=== Iterator Arithmetic ===" << endl;
    auto it = vec.begin();
    cout << "First element: " << *it << endl;
    cout << "Third element: " << *(it + 2) << endl;
    cout << "Last element: " << *(vec.end() - 1) << endl;
    
    // Distance between iterators
    cout << "Distance from begin to end: " << distance(vec.begin(), vec.end()) << endl;
    
    // Advance iterator
    auto mid_it = vec.begin();
    advance(mid_it, 2);
    cout << "Element at position 2: " << *mid_it << endl;
    
    // Next and prev iterators
    auto next_it = next(vec.begin(), 3);
    auto prev_it = prev(vec.end(), 2);
    cout << "Next(begin, 3): " << *next_it << endl;
    cout << "Prev(end, 2): " << *prev_it << endl;
    
    // Modifying elements through iterators
    cout << "\n=== Modifying Elements ===" << endl;
    for (auto it = vec.begin(); it != vec.end(); ++it) {
        *it *= 2; // Double each element
    }
    
    cout << "After doubling: ";
    for (const auto& elem : vec) {
        cout << elem << " ";
    }
    cout << endl;
    
    // Bidirectional iterator (list)
    cout << "\n=== Bidirectional Iterator (list) ===" << endl;
    list<string> lst = {"apple", "banana", "cherry", "date"};
    
    cout << "List forward: ";
    for (auto it = lst.begin(); it != lst.end(); ++it) {
        cout << *it << " ";
    }
    cout << endl;
    
    cout << "List backward: ";
    for (auto it = lst.rbegin(); it != lst.rend(); ++it) {
        cout << *it << " ";
    }
    cout << endl;
    
    // Finding in list
    auto found = find(lst.begin(), lst.end(), "cherry");
    if (found != lst.end()) {
        cout << "Found: " << *found << endl;
        // Cannot use arithmetic with bidirectional iterators
        // cout << *(found + 1); // Error!
        auto next_elem = next(found);
        if (next_elem != lst.end()) {
            cout << "Next element: " << *next_elem << endl;
        }
    }
    
    // Forward iterator (unordered_set)
    cout << "\n=== Forward Iterator (unordered_set) ===" << endl;
    unordered_set<int> uset = {5, 2, 8, 1, 9};
    cout << "Unordered set elements: ";
    for (auto it = uset.begin(); it != uset.end(); ++it) {
        cout << *it << " ";
    }
    cout << endl;
    
    // Map iterators
    cout << "\n=== Map Iterators ===" << endl;
    map<string, int> grades = {{"Alice", 95}, {"Bob", 87}, {"Charlie", 92}};
    
    cout << "Map elements:" << endl;
    for (auto it = grades.begin(); it != grades.end(); ++it) {
        cout << it->first << ": " << it->second << endl;
        // Alternatively: (*it).first, (*it).second
    }
    
    // Lower and upper bounds with iterators
    auto lower = grades.lower_bound("Bob");
    auto upper = grades.upper_bound("Bob");
    cout << "Lower bound for Bob: " << lower->first << endl;
    if (upper != grades.end()) {
        cout << "Upper bound for Bob: " << upper->first << endl;
    }
    
    // Insert iterators
    cout << "\n=== Insert Iterators ===" << endl;
    vector<int> source = {1, 2, 3, 4, 5};
    vector<int> dest;
    
    // Back insert iterator
    copy(source.begin(), source.end(), back_inserter(dest));
    cout << "After back_inserter: ";
    for (const auto& elem : dest) {
        cout << elem << " ";
    }
    cout << endl;
    
    // Front insert iterator (with list)
    list<int> destList;
    copy(source.begin(), source.end(), front_inserter(destList));
    cout << "After front_inserter: ";
    for (const auto& elem : destList) {
        cout << elem << " ";
    }
    cout << endl;
    
    // General insert iterator
    vector<int> destInsert = {100, 200};
    auto insert_pos = destInsert.begin() + 1;
    copy(source.begin(), source.begin() + 3, inserter(destInsert, insert_pos));
    cout << "After inserter: ";
    for (const auto& elem : destInsert) {
        cout << elem << " ";
    }
    cout << endl;
    
    // Stream iterators
    cout << "\n=== Stream Iterators ===" << endl;
    vector<int> numbers = {1, 2, 3, 4, 5};
    
    cout << "Using ostream_iterator: ";
    copy(numbers.begin(), numbers.end(), ostream_iterator<int>(cout, " "));
    cout << endl;
    
    // Input stream iterator
    cout << "Enter some integers (end with non-integer): ";
    vector<int> input_numbers;
    copy(istream_iterator<int>(cin), istream_iterator<int>(), back_inserter(input_numbers));
    
    cout << "You entered: ";
    copy(input_numbers.begin(), input_numbers.end(), ostream_iterator<int>(cout, " "));
    cout << endl;
    
    // String stream with iterators
    stringstream ss("10 20 30 40 50");
    vector<int> stream_numbers;
    copy(istream_iterator<int>(ss), istream_iterator<int>(), back_inserter(stream_numbers));
    
    cout << "From stringstream: ";
    for (const auto& num : stream_numbers) {
        cout << num << " ";
    }
    cout << endl;
    
    // Reverse iterators
    cout << "\n=== Reverse Iterators ===" << endl;
    vector<int> vals = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
    
    // Find last even number
    auto rev_it = find_if(vals.rbegin(), vals.rend(), [](int n) { return n % 2 == 0; });
    if (rev_it != vals.rend()) {
        cout << "Last even number: " << *rev_it << endl;
        // Convert reverse iterator to normal iterator
        auto normal_it = rev_it.base();
        cout << "Position from begin: " << distance(vals.begin(), normal_it) - 1 << endl;
    }
    
    // Iterator invalidation example
    cout << "\n=== Iterator Safety ===" << endl;
    vector<int> safety_vec = {1, 2, 3, 4, 5};
    auto safe_it = safety_vec.begin() + 2;
    cout << "Before push_back: " << *safe_it << endl;
    
    // This might invalidate iterators if reallocation occurs
    safety_vec.reserve(20); // Ensure no reallocation
    safety_vec.push_back(6);
    cout << "After push_back (with reserve): " << *safe_it << endl;
    
    return 0;
}

Iterator Safety Tips:

  • Iterators can be invalidated by container modifications
  • Use const_iterator when you don't need to modify elements
  • Prefer range-based for loops when iterator arithmetic isn't needed
  • Be careful with iterator arithmetic - only works with random access iterators

Algorithms

STL algorithms are template functions that perform operations on ranges of elements. They work with any container that provides the appropriate iterators, making them highly reusable and efficient.

Algorithm Categories:

Non-modifying

Read elements without changing them

find, count, equal, search, for_each

Modifying

Change or rearrange elements

copy, move, transform, replace, remove

Sorting

Arrange elements in order

sort, stable_sort, partial_sort, nth_element

Numeric

Mathematical operations

accumulate, adjacent_difference, inner_product

Set Operations

Work with sorted ranges

merge, set_union, set_intersection

Time Complexity Guide:

O(1) - swap, min, max
O(n) - find, count, copy, transform
O(n log n) - sort, stable_sort, merge
O(log n) - binary_search, lower_bound
algorithms_example.cpp
#include <iostream>
#include <vector>
#include <algorithm>
#include <numeric>
#include <string>
#include <functional>
#include <iterator>
#include <list>
#include <deque>
#include <set>
#include <random>
#include <execution>
using namespace std;

// Custom predicate functions
bool isEven(int n) { return n % 2 == 0; }
bool isOdd(int n) { return n % 2 == 1; }

struct Person {
    string name;
    int age;
    double salary;
    
    Person(string n, int a, double s) : name(n), age(a), salary(s) {}
};

int main() {
    // Sorting algorithms - The foundation of many operations
    cout << "=== Sorting Algorithms ===" << endl;
    vector<int> numbers = {5, 2, 8, 1, 9, 3, 7, 4, 6};
    
    cout << "Original: ";
    for (const auto& n : numbers) cout << n << " ";
    cout << endl;
    
    // Sort ascending
    sort(numbers.begin(), numbers.end());
    cout << "Sort ascending: ";
    for (const auto& n : numbers) cout << n << " ";
    cout << endl;
    
    // Sort descending
    sort(numbers.begin(), numbers.end(), greater<int>());
    cout << "Sort descending: ";
    for (const auto& n : numbers) cout << n << " ";
    cout << endl;
    
    // Stable sort (preserves relative order of equal elements)
    vector<Person> people = {
        {"Alice", 25, 50000}, {"Bob", 30, 60000}, {"Charlie", 25, 55000}
    };
    
    stable_sort(people.begin(), people.end(), 
                [](const Person& a, const Person& b) { return a.age < b.age; });
    cout << "Stable sort by age:" << endl;
    for (const auto& p : people) {
        cout << p.name << " (age " << p.age << ")" << endl;
    }
    
    // Partial sort - only sort first N elements
    vector<int> partial_nums = {9, 5, 2, 8, 1, 7, 3, 6, 4};
    partial_sort(partial_nums.begin(), partial_nums.begin() + 3, partial_nums.end());
    cout << "Partial sort (first 3): ";
    for (const auto& n : partial_nums) cout << n << " ";
    cout << endl;
    
    // nth_element - partition around nth element
    vector<int> nth_nums = {9, 5, 2, 8, 1, 7, 3, 6, 4};
    nth_element(nth_nums.begin(), nth_nums.begin() + 4, nth_nums.end());
    cout << "After nth_element (4th position): ";
    for (const auto& n : nth_nums) cout << n << " ";
    cout << " (5th element: " << nth_nums[4] << ")" << endl;
    
    // Searching algorithms
    cout << "\n=== Searching Algorithms ===" << endl;
    vector<int> data = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
    
    // Linear search
    auto it = find(data.begin(), data.end(), 5);
    if (it != data.end()) {
        cout << "Linear search - Found 5 at position: " << distance(data.begin(), it) << endl;
    }
    
    // Find with predicate
    auto even_it = find_if(data.begin(), data.end(), isEven);
    if (even_it != data.end()) {
        cout << "First even number: " << *even_it << endl;
    }
    
    // Find if not
    auto odd_it = find_if_not(data.begin(), data.end(), isEven);
    if (odd_it != data.end()) {
        cout << "First odd number: " << *odd_it << endl;
    }
    
    // Binary search (requires sorted data)
    bool found = binary_search(data.begin(), data.end(), 7);
    cout << "Binary search for 7: " << (found ? "Found" : "Not found") << endl;
    
    // Lower and upper bound
    auto lower = lower_bound(data.begin(), data.end(), 5);
    auto upper = upper_bound(data.begin(), data.end(), 5);
    cout << "Lower bound of 5: position " << distance(data.begin(), lower) << endl;
    cout << "Upper bound of 5: position " << distance(data.begin(), upper) << endl;
    
    // Equal range
    auto range = equal_range(data.begin(), data.end(), 5);
    cout << "Equal range for 5: [" << distance(data.begin(), range.first) 
         << ", " << distance(data.begin(), range.second) << ")" << endl;
    
    // Search for subsequence
    vector<int> pattern = {3, 4, 5};
    auto sub_it = search(data.begin(), data.end(), pattern.begin(), pattern.end());
    if (sub_it != data.end()) {
        cout << "Found pattern {3,4,5} at position: " << distance(data.begin(), sub_it) << endl;
    }
    
    // Counting algorithms
    cout << "\n=== Counting Algorithms ===" << endl;
    vector<int> values = {1, 2, 3, 2, 4, 2, 5, 2, 6};
    
    int count2 = count(values.begin(), values.end(), 2);
    cout << "Count of 2: " << count2 << endl;
    
    int countEven = count_if(values.begin(), values.end(), isEven);
    cout << "Count of even numbers: " << countEven << endl;
    
    // All, any, none algorithms
    bool allPositive = all_of(values.begin(), values.end(), [](int n) { return n > 0; });
    bool anyGreaterThan5 = any_of(values.begin(), values.end(), [](int n) { return n > 5; });
    bool noneNegative = none_of(values.begin(), values.end(), [](int n) { return n < 0; });
    
    cout << "All positive: " << allPositive << endl;
    cout << "Any greater than 5: " << anyGreaterThan5 << endl;
    cout << "None negative: " << noneNegative << endl;
    
    // Transformation algorithms
    cout << "\n=== Transformation Algorithms ===" << endl;
    vector<int> source = {1, 2, 3, 4, 5};
    vector<int> squares;
    
    // Transform to squares
    transform(source.begin(), source.end(), back_inserter(squares),
              [](int n) { return n * n; });
    
    cout << "Original: ";
    for (const auto& n : source) cout << n << " ";
    cout << endl;
    
    cout << "Squares: ";
    for (const auto& n : squares) cout << n << " ";
    cout << endl;
    
    // Binary transform (two input ranges)
    vector<int> source2 = {10, 20, 30, 40, 50};
    vector<int> sums;
    
    transform(source.begin(), source.end(), source2.begin(), back_inserter(sums),
              [](int a, int b) { return a + b; });
    
    cout << "Sums: ";
    for (const auto& n : sums) cout << n << " ";
    cout << endl;
    
    // Replace algorithms
    vector<int> replace_vec = {1, 2, 3, 2, 4, 2, 5};
    replace(replace_vec.begin(), replace_vec.end(), 2, 99);
    cout << "After replace(2, 99): ";
    for (const auto& n : replace_vec) cout << n << " ";
    cout << endl;
    
    // Replace if
    replace_if(replace_vec.begin(), replace_vec.end(), 
               [](int n) { return n > 50; }, 0);
    cout << "After replace_if(>50, 0): ";
    for (const auto& n : replace_vec) cout << n << " ";
    cout << endl;
    
    // Copy algorithms
    cout << "\n=== Copy Algorithms ===" << endl;
    vector<int> copy_source = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
    vector<int> copy_dest;
    
    // Copy all
    copy(copy_source.begin(), copy_source.end(), back_inserter(copy_dest));
    cout << "Copy all: ";
    for (const auto& n : copy_dest) cout << n << " ";
    cout << endl;
    
    // Copy if
    vector<int> even_dest;
    copy_if(copy_source.begin(), copy_source.end(), back_inserter(even_dest), isEven);
    cout << "Copy even: ";
    for (const auto& n : even_dest) cout << n << " ";
    cout << endl;
    
    // Copy n elements
    vector<int> copy_n_dest;
    copy_n(copy_source.begin(), 5, back_inserter(copy_n_dest));
    cout << "Copy first 5: ";
    for (const auto& n : copy_n_dest) cout << n << " ";
    cout << endl;
    
    // Remove algorithms
    cout << "\n=== Remove Algorithms ===" << endl;
    vector<int> remove_vec = {1, 2, 3, 2, 4, 2, 5, 2, 6};
    
    cout << "Before remove: ";
    for (const auto& n : remove_vec) cout << n << " ";
    cout << endl;
    
    // Remove doesn't actually erase, it moves elements
    auto new_end = remove(remove_vec.begin(), remove_vec.end(), 2);
    cout << "After remove(2): ";
    for (auto it = remove_vec.begin(); it != new_end; ++it) cout << *it << " ";
    cout << endl;
    
    // Erase-remove idiom for actual removal
    remove_vec.erase(new_end, remove_vec.end());
    cout << "After erase: ";
    for (const auto& n : remove_vec) cout << n << " ";
    cout << endl;
    
    // Numeric algorithms
    cout << "\n=== Numeric Algorithms ===" << endl;
    vector<int> nums = {1, 2, 3, 4, 5};
    
    int sum = accumulate(nums.begin(), nums.end(), 0);
    cout << "Sum: " << sum << endl;
    
    int product = accumulate(nums.begin(), nums.end(), 1, multiplies<int>());
    cout << "Product: " << product << endl;
    
    // Reduce (C++17) - parallel version of accumulate
    int sum2 = reduce(nums.begin(), nums.end(), 0);
    cout << "Reduce sum: " << sum2 << endl;
    
    // Partial sum
    vector<int> partialSums;
    partial_sum(nums.begin(), nums.end(), back_inserter(partialSums));
    cout << "Partial sums: ";
    for (const auto& n : partialSums) cout << n << " ";
    cout << endl;
    
    // Adjacent difference
    vector<int> differences;
    adjacent_difference(nums.begin(), nums.end(), back_inserter(differences));
    cout << "Adjacent differences: ";
    for (const auto& n : differences) cout << n << " ";
    cout << endl;
    
    // Iota - fill with consecutive values
    vector<int> iota_vec(10);
    iota(iota_vec.begin(), iota_vec.end(), 1);
    cout << "Iota (1-10): ";
    for (const auto& n : iota_vec) cout << n << " ";
    cout << endl;
    
    // Inner product
    vector<int> vec1 = {1, 2, 3, 4};
    vector<int> vec2 = {5, 6, 7, 8};
    int dot_product = inner_product(vec1.begin(), vec1.end(), vec2.begin(), 0);
    cout << "Dot product: " << dot_product << endl;
    
    // Min/Max algorithms
    cout << "\n=== Min/Max Algorithms ===" << endl;
    vector<int> elements = {3, 1, 4, 1, 5, 9, 2, 6};
    
    auto minIt = min_element(elements.begin(), elements.end());
    auto maxIt = max_element(elements.begin(), elements.end());
    auto minMaxPair = minmax_element(elements.begin(), elements.end());
    
    cout << "Min element: " << *minIt << " at position " << distance(elements.begin(), minIt) << endl;
    cout << "Max element: " << *maxIt << " at position " << distance(elements.begin(), maxIt) << endl;
    cout << "MinMax: min=" << *minMaxPair.first << ", max=" << *minMaxPair.second << endl;
    
    // Clamp (C++17)
    cout << "Clamp 15 to [1,10]: " << clamp(15, 1, 10) << endl;
    cout << "Clamp -5 to [1,10]: " << clamp(-5, 1, 10) << endl;
    
    // Permutation algorithms
    cout << "\n=== Permutation Algorithms ===" << endl;
    vector<int> perm = {1, 2, 3};
    
    cout << "All permutations of {1, 2, 3}:" << endl;
    do {
        for (const auto& n : perm) cout << n << " ";
        cout << endl;
    } while (next_permutation(perm.begin(), perm.end()));
    
    // Check if one sequence is a permutation of another
    vector<int> seq1 = {1, 2, 3, 4};
    vector<int> seq2 = {4, 3, 2, 1};
    bool is_perm = is_permutation(seq1.begin(), seq1.end(), seq2.begin());
    cout << "Is {4,3,2,1} a permutation of {1,2,3,4}? " << is_perm << endl;
    
    // Set operations (require sorted input)
    cout << "\n=== Set Operations ===" << endl;
    vector<int> set1 = {1, 2, 3, 4, 5};
    vector<int> set2 = {3, 4, 5, 6, 7};
    vector<int> result;
    
    // Union
    set_union(set1.begin(), set1.end(), set2.begin(), set2.end(), back_inserter(result));
    cout << "Union: ";
    for (const auto& n : result) cout << n << " ";
    cout << endl;
    
    // Intersection
    result.clear();
    set_intersection(set1.begin(), set1.end(), set2.begin(), set2.end(), back_inserter(result));
    cout << "Intersection: ";
    for (const auto& n : result) cout << n << " ";
    cout << endl;
    
    // Difference
    result.clear();
    set_difference(set1.begin(), set1.end(), set2.begin(), set2.end(), back_inserter(result));
    cout << "Difference (set1 - set2): ";
    for (const auto& n : result) cout << n << " ";
    cout << endl;
    
    // Symmetric difference
    result.clear();
    set_symmetric_difference(set1.begin(), set1.end(), set2.begin(), set2.end(), back_inserter(result));
    cout << "Symmetric difference: ";
    for (const auto& n : result) cout << n << " ";
    cout << endl;
    
    // Heap operations
    cout << "\n=== Heap Operations ===" << endl;
    vector<int> heap_vec = {3, 1, 4, 1, 5, 9, 2, 6};
    
    // Make heap
    make_heap(heap_vec.begin(), heap_vec.end());
    cout << "After make_heap: ";
    for (const auto& n : heap_vec) cout << n << " ";
    cout << " (max: " << heap_vec.front() << ")" << endl;
    
    // Push to heap
    heap_vec.push_back(10);
    push_heap(heap_vec.begin(), heap_vec.end());
    cout << "After push 10: max = " << heap_vec.front() << endl;
    
    // Pop from heap
    pop_heap(heap_vec.begin(), heap_vec.end());
    int max_val = heap_vec.back();
    heap_vec.pop_back();
    cout << "Popped max: " << max_val << ", new max: " << heap_vec.front() << endl;
    
    // Sort heap
    sort_heap(heap_vec.begin(), heap_vec.end());
    cout << "After sort_heap: ";
    for (const auto& n : heap_vec) cout << n << " ";
    cout << endl;
    
    // Shuffle algorithm
    cout << "\n=== Shuffle Algorithm ===" << endl;
    vector<int> shuffle_vec = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
    random_device rd;
    mt19937 g(rd());
    
    cout << "Original: ";
    for (const auto& n : shuffle_vec) cout << n << " ";
    cout << endl;
    
    shuffle(shuffle_vec.begin(), shuffle_vec.end(), g);
    cout << "Shuffled: ";
    for (const auto& n : shuffle_vec) cout << n << " ";
    cout << endl;
    
    return 0;
}

Algorithm Best Practices:

  • Always use the most specific algorithm for your task (e.g., binary_search vs find)
  • Prefer algorithms over hand-written loops - they're optimized and less error-prone
  • Use lambda expressions for simple predicates and transformations
  • Remember the erase-remove idiom for actually removing elements
  • Consider parallel algorithms (C++17) for large datasets

Function Objects

Function objects (functors) are objects that can be called like functions. They provide a way to encapsulate function-like behavior in objects and are fundamental to STL's flexibility.

Types of Function Objects

Type Syntax Performance Use Cases
Lambda Expressions [capture](params) { body } Excellent (inlined) Modern C++, local use
Built-in Functors std::plus<T>() Excellent (optimized) Standard operations
Custom Functors operator() Excellent (inlined) Complex state, reusability
Function Pointers RetType (*func)(Args) Good (call overhead) C compatibility
std::function std::function<Sig> Fair (type erasure) Type erasure, storage
function_objects_comprehensive.cpp
#include <iostream>
#include <vector>
#include <algorithm>
#include <functional>
#include <string>
#include <memory>
using namespace std;

// Custom functor classes
class Multiplier {
    int factor;
public:
    Multiplier(int f) : factor(f) {}
    int operator()(int x) const { return x * factor; }
    
    // For demonstration - functors can have state
    void setFactor(int f) { factor = f; }
    int getFactor() const { return factor; }
};

class Accumulator {
    mutable int sum = 0;  // mutable allows modification in const operator()
public:
    int operator()(int value) const {
        sum += value;
        return sum;
    }
    int getSum() const { return sum; }
};

// Predicate functors
struct IsEven {
    bool operator()(int n) const { return n % 2 == 0; }
};

struct InRange {
    int min, max;
    InRange(int mn, int mx) : min(mn), max(mx) {}
    bool operator()(int n) const { return n >= min && n <= max; }
};

int main() {
    cout << "=== Lambda Expressions ===" << endl;
    vector<int> numbers = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
    
    // Basic lambda
    auto square = [](int x) { return x * x; };
    cout << "Square of 5: " << square(5) << endl;
    
    // Lambda with capture by value
    int multiplier = 3;
    auto multiplyBy = [multiplier](int x) { return x * multiplier; };
    cout << "7 * 3 = " << multiplyBy(7) << endl;
    
    // Lambda with capture by reference
    int sum = 0;
    auto addToSum = [&sum](int x) { sum += x; return sum; };
    cout << "Adding 5 to sum: " << addToSum(5) << endl;
    cout << "Adding 10 to sum: " << addToSum(10) << endl;
    
    // Lambda with mutable (allows modifying captured-by-value variables)
    int counter = 0;
    auto increment = [counter]() mutable { return ++counter; };
    cout << "Counter: " << increment() << ", " << increment() << ", " << increment() << endl;
    cout << "Original counter still: " << counter << endl;
    
    // Generic lambda (C++14)
    auto genericAdd = [](auto a, auto b) { return a + b; };
    cout << "Generic add (int): " << genericAdd(5, 3) << endl;
    cout << "Generic add (double): " << genericAdd(2.5, 1.5) << endl;
    cout << "Generic add (string): " << genericAdd(string("Hello "), string("World")) << endl;
    
    // Lambda with explicit return type
    auto divide = [](double a, double b) -> double {
        if (b == 0) throw invalid_argument("Division by zero");
        return a / b;
    };
    cout << "10.0 / 3.0 = " << divide(10.0, 3.0) << endl;
    
    // Capture all by value and reference
    int a = 10, b = 20;
    auto captureAll = [=, &b](int x) { b += x; return a + b + x; };  // a by value, b by reference
    cout << "Capture example: " << captureAll(5) << " (b is now " << b << ")" << endl;
    
    // Immediately invoked lambda expression (IILE)
    int result = [](int x, int y) { return x * y + 10; }(5, 3);
    cout << "IILE result: " << result << endl;
    
    cout << "\n=== Built-in Functors ===" << endl;
    
    // Arithmetic functors
    plus<int> add;
    minus<int> subtract;
    multiplies<int> multiply;
    divides<int> divide_op;
    modulus<int> mod;
    negate<int> neg;
    
    cout << "5 + 3 = " << add(5, 3) << endl;
    cout << "5 - 3 = " << subtract(5, 3) << endl;
    cout << "5 * 3 = " << multiply(5, 3) << endl;
    cout << "15 / 3 = " << divide_op(15, 3) << endl;
    cout << "15 % 4 = " << mod(15, 4) << endl;
    cout << "-5 = " << neg(5) << endl;
    
    // Comparison functors
    equal_to<int> eq;
    not_equal_to<int> neq;
    greater<int> gt;
    less<int> lt;
    greater_equal<int> gte;
    less_equal<int> lte;
    
    cout << "5 == 5: " << eq(5, 5) << endl;
    cout << "5 != 3: " << neq(5, 3) << endl;
    cout << "5 > 3: " << gt(5, 3) << endl;
    
    // Logical functors
    logical_and<bool> and_op;
    logical_or<bool> or_op;
    logical_not<bool> not_op;
    
    cout << "true && false: " << and_op(true, false) << endl;
    cout << "true || false: " << or_op(true, false) << endl;
    cout << "!true: " << not_op(true) << endl;
    
    // Using functors with algorithms
    vector<int> nums = {5, 2, 8, 1, 9};
    sort(nums.begin(), nums.end(), greater<int>());
    cout << "Sorted descending: ";
    for (int n : nums) cout << n << " ";
    cout << endl;
    
    cout << "\n=== Custom Functors ===" << endl;
    
    // Using custom functors
    Multiplier times3(3);
    vector<int> source = {1, 2, 3, 4, 5};
    vector<int> multiplied;
    
    transform(source.begin(), source.end(), back_inserter(multiplied), times3);
    cout << "Multiplied by 3: ";
    for (int n : multiplied) cout << n << " ";
    cout << endl;
    
    // Functor with state
    Accumulator acc;
    cout << "Accumulating: ";
    for (int i = 1; i <= 5; ++i) {
        cout << acc(i) << " ";
    }
    cout << "(final sum: " << acc.getSum() << ")" << endl;
    
    // Predicate functors
    IsEven evenChecker;
    InRange rangeChecker(3, 7);
    
    auto evenCount = count_if(source.begin(), source.end(), evenChecker);
    auto inRangeCount = count_if(source.begin(), source.end(), rangeChecker);
    
    cout << "Even numbers in source: " << evenCount << endl;
    cout << "Numbers in range [3,7]: " << inRangeCount << endl;
    
    cout << "\n=== std::function and Type Erasure ===" << endl;
    
    // std::function can hold any callable
    function<int(int)> func;
    
    // Assign lambda
    func = [](int x) { return x * 2; };
    cout << "Lambda: " << func(5) << endl;
    
    // Assign functor
    func = times3;
    cout << "Functor: " << func(5) << endl;
    
    // Assign function pointer
    func = [](int x) { return x + 10; };
    cout << "Another lambda: " << func(5) << endl;
    
    // Vector of functions
    vector<function<int(int)>> operations = {
        [](int x) { return x * x; },           // square
        [](int x) { return x * 2; },           // double
        [](int x) { return x + 1; },           // increment
        Multiplier(5)                          // multiply by 5
    };
    
    cout << "Applying operations to 3: ";
    for (const auto& op : operations) {
        cout << op(3) << " ";
    }
    cout << endl;
    
    cout << "\n=== Function Binding ===" << endl;
    
    // Function to bind
    auto addThree = [](int a, int b, int c) { return a + b + c; };
    
    // Bind some arguments
    auto addTo10And20 = bind(addThree, placeholders::_1, 10, 20);
    cout << "Adding 5 to (10 + 20): " << addTo10And20(5) << endl;
    
    // Bind with reordered arguments
    auto reorderedAdd = bind(addThree, placeholders::_3, placeholders::_1, placeholders::_2);
    cout << "Reordered add(1,2,3) as (3,1,2): " << reorderedAdd(1, 2, 3) << endl;
    
    // Bind member function
    struct Calculator {
        int multiply(int a, int b) const { return a * b; }
    };
    
    Calculator calc;
    auto boundMultiply = bind(&Calculator::multiply, &calc, placeholders::_1, placeholders::_2);
    cout << "Bound member function: " << boundMultiply(6, 7) << endl;
    
    cout << "\n=== Advanced Function Object Techniques ===" << endl;
    
    // Function composition
    auto compose = [](auto f, auto g) {
        return [f, g](auto x) { return f(g(x)); };
    };
    
    auto increment = [](int x) { return x + 1; };
    auto square_func = [](int x) { return x * x; };
    auto incrementThenSquare = compose(square_func, increment);
    
    cout << "Compose square(increment(5)): " << incrementThenSquare(5) << endl;
    
    // Currying
    auto curry = [](auto f) {
        return [f](auto a) {
            return [f, a](auto b) {
                return f(a, b);
            };
        };
    };
    
    auto curriedAdd = curry([](int a, int b) { return a + b; });
    auto add5 = curriedAdd(5);
    cout << "Curried add(5)(3): " << add5(3) << endl;
    
    // Higher-order function
    auto applyTwice = [](auto f) {
        return [f](auto x) { return f(f(x)); };
    };
    
    auto doubleFunc = [](int x) { return x * 2; };
    auto doubleTwice = applyTwice(doubleFunc);
    cout << "Apply double twice to 3: " << doubleTwice(3) << endl;
    
    return 0;
}

Function Object Best Practices:

  • Prefer lambdas for simple, local operations
  • Use custom functors when you need state or complex logic
  • std::function is great for type erasure but has performance overhead
  • Capture by reference [&] for large objects, by value [=] for primitives
  • Use mutable lambdas sparingly - prefer external state management
  • Consider generic lambdas for template-like behavior

Utilities

STL provides various utility classes and functions for common programming tasks, including smart pointers, type traits, time utilities, and modern C++ features.

Utility Categories

Category Key Components C++ Version Primary Use Cases
Pairs & Tuples std::pair, std::tuple C++98/C++11 Multiple return values, data grouping
Smart Pointers unique_ptr, shared_ptr, weak_ptr C++11 Automatic memory management
Type Traits is_same, enable_if, conditional C++11/C++14 Template metaprogramming
Time & Chrono chrono::duration, time_point C++11 Time measurement, timing
Random Numbers random_device, mt19937 C++11 High-quality random generation
Optional & Variant std::optional, std::variant C++17 Safe optionals, type-safe unions
utilities_comprehensive.cpp
#include <iostream>
#include <utility>
#include <tuple>
#include <string>
#include <vector>
#include <algorithm>
#include <chrono>
#include <random>
#include <memory>
#include <optional>
#include <variant>
#include <type_traits>
#include <functional>
#include <any>
#include <string_view>
#include <span>
using namespace std;

// Example classes for demonstrations
class Resource {
public:
    Resource(const string& name) : name_(name) {
        cout << "Resource '" << name_ << "' created\n";
    }
    
    ~Resource() {
        cout << "Resource '" << name_ << "' destroyed\n";
    }
    
    void use() const {
        cout << "Using resource: " << name_ << "\n";
    }
    
    string getName() const { return name_; }
    
private:
    string name_;
};

// Function returning multiple values using pair
pair<bool, int> divideWithRemainder(int a, int b) {
    if (b == 0) return {false, 0};
    return {true, a % b};
}

// Function returning multiple values using tuple
tuple<double, double, double> calculateStats(const vector<int>& data) {
    if (data.empty()) return {0.0, 0.0, 0.0};
    
    double sum = accumulate(data.begin(), data.end(), 0.0);
    double mean = sum / data.size();
    
    double variance = 0.0;
    for (const auto& val : data) {
        variance += (val - mean) * (val - mean);
    }
    variance /= data.size();
    
    double stddev = sqrt(variance);
    return {mean, variance, stddev};
}

// Template function demonstrating type traits
template<typename T>
void processNumber(T value) {
    if constexpr (is_integral_v<T>) {
        cout << "Processing integer: " << value << " (doubled: " << value * 2 << ")\n";
    } else if constexpr (is_floating_point_v<T>) {
        cout << "Processing float: " << value << " (squared: " << value * value << ")\n";
    } else {
        cout << "Processing other type\n";
    }
}

int main() {
    cout << "=== Pairs and Tuples ===" << endl;
    
    // Pair examples
    pair<string, int> person("Alice", 25);
    cout << "Person: " << person.first << ", Age: " << person.second << endl;
    
    auto coordinates = make_pair(10.5, 20.3);
    cout << "Coordinates: (" << coordinates.first << ", " << coordinates.second << ")" << endl;
    
    // Using pair for multiple return values
    auto [success, remainder] = divideWithRemainder(17, 5);
    if (success) {
        cout << "17 % 5 = " << remainder << endl;
    }
    
    // Tuple examples
    tuple<string, int, double, bool> student("Bob", 22, 3.8, true);
    cout << "Student: " << get<0>(student) << ", Age: " << get<1>(student) 
         << ", GPA: " << get<2>(student) << ", Enrolled: " << get<3>(student) << endl;
    
    // Structured binding (C++17)
    auto [name, age, gpa, enrolled] = student;
    cout << "Using structured binding - Name: " << name << ", Age: " << age << endl;
    
    // Tuple from function
    vector<int> data = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
    auto [mean, variance, stddev] = calculateStats(data);
    cout << "Stats - Mean: " << mean << ", Variance: " << variance << ", StdDev: " << stddev << endl;
    
    // Tuple operations
    cout << "Tuple size: " << tuple_size_v<decltype(student)> << endl;
    
    cout << "\n=== Smart Pointers ===" << endl;
    
    // unique_ptr - exclusive ownership
    cout << "-- unique_ptr --" << endl;
    {
        auto resource1 = make_unique<Resource>("UniqueResource");
        resource1->use();
        
        // Transfer ownership
        auto resource2 = move(resource1);
        if (!resource1) {
            cout << "resource1 is now null after move" << endl;
        }
        resource2->use();
        
        // Custom deleter
        auto customDeleter = [](Resource* r) {
            cout << "Custom deleter called for: " << r->getName() << endl;
            delete r;
        };
        unique_ptr<Resource, decltype(customDeleter)> customResource(
            new Resource("CustomDeleted"), customDeleter
        );
    } // Resources automatically destroyed here
    
    cout << "-- shared_ptr --" << endl;
    {
        auto shared1 = make_shared<Resource>("SharedResource");
        cout << "Reference count: " << shared1.use_count() << endl;
        
        {
            auto shared2 = shared1;  // Copy increases reference count
            cout << "Reference count after copy: " << shared1.use_count() << endl;
            shared2->use();
        } // shared2 goes out of scope
        
        cout << "Reference count after shared2 destroyed: " << shared1.use_count() << endl;
        
        // weak_ptr to break cycles
        weak_ptr<Resource> weak = shared1;
        cout << "weak_ptr expired: " << weak.expired() << endl;
        
        if (auto locked = weak.lock()) {
            locked->use();
            cout << "Successfully locked weak_ptr" << endl;
        }
    }
    
    cout << "\n=== Optional and Variant ===" << endl;
    
    // Optional (C++17)
    auto safeDivide = [](double a, double b) -> optional<double> {
        if (b == 0.0) return nullopt;
        return a / b;
    };
    
    auto result1 = safeDivide(10.0, 2.0);
    auto result2 = safeDivide(10.0, 0.0);
    
    if (result1) {
        cout << "Division result: " << *result1 << endl;
    }
    
    cout << "Safe division by zero: " << result2.value_or(-1.0) << endl;
    
    // Variant (C++17) - type-safe union
    variant<int, double, string> value;
    
    value = 42;
    cout << "Variant holds int: " << get<int>(value) << endl;
    
    value = 3.14;
    cout << "Variant holds double: " << get<double>(value) << endl;
    
    value = string("Hello");
    cout << "Variant holds string: " << get<string>(value) << endl;
    
    // Visiting variant
    auto visitor = [](const auto& v) {
        cout << "Visiting value: " << v << " (type: " << typeid(v).name() << ")" << endl;
    };
    visit(visitor, value);
    
    cout << "\n=== Type Traits and SFINAE ===" << endl;
    
    // Basic type checking
    cout << "is_integral<int>: " << is_integral_v<int> << endl;
    cout << "is_floating_point<double>: " << is_floating_point_v<double> << endl;
    cout << "is_same<int, int32_t>: " << is_same_v<int, int32_t> << endl;
    cout << "is_pointer<int*>: " << is_pointer_v<int*> << endl;
    
    // Template function with type traits
    processNumber(42);        // int
    processNumber(3.14);      // double
    processNumber("hello");   // const char*
    
    // enable_if example
    auto processContainer = []<typename T>(const T& container) {
        if constexpr (is_same_v<T, vector<typename T::value_type>>) {
            cout << "Processing vector with " << container.size() << " elements" << endl;
        } else {
            cout << "Processing other container type" << endl;
        }
    };
    
    vector<int> vec = {1, 2, 3, 4, 5};
    processContainer(vec);
    
    cout << "\n=== Chrono Utilities ===" << endl;
    
    // Time measurement
    auto start = chrono::high_resolution_clock::now();
    
    // Simulate work
    vector<int> largeVec(100000);
    iota(largeVec.begin(), largeVec.end(), 1);
    sort(largeVec.begin(), largeVec.end(), greater<int>());
    
    auto end = chrono::high_resolution_clock::now();
    
    // Different time units
    auto microsecs = chrono::duration_cast<chrono::microseconds>(end - start);
    auto millisecs = chrono::duration_cast<chrono::milliseconds>(end - start);
    auto nanosecs = chrono::duration_cast<chrono::nanoseconds>(end - start);
    
    cout << "Sorting took:" << endl;
    cout << "  " << microsecs.count() << " microseconds" << endl;
    cout << "  " << millisecs.count() << " milliseconds" << endl;
    cout << "  " << nanosecs.count() << " nanoseconds" << endl;
    
    // Time points and durations
    using namespace chrono_literals;
    auto now = chrono::system_clock::now();
    auto future = now + 1h + 30min + 45s;
    
    cout << "Time calculations work with literals!" << endl;
    
    cout << "\n=== Random Number Generation ===" << endl;
    
    random_device rd;  // Hardware random number generator
    mt19937 gen(rd()); // Mersenne Twister generator
    
    // Different distributions
    uniform_int_distribution<int> intDist(1, 100);
    uniform_real_distribution<double> realDist(0.0, 1.0);
    normal_distribution<double> normalDist(50.0, 15.0);  // mean=50, stddev=15
    bernoulli_distribution coinFlip(0.5);
    
    cout << "Uniform integers (1-100): ";
    for (int i = 0; i < 5; ++i) cout << intDist(gen) << " ";
    cout << endl;
    
    cout << "Uniform reals (0.0-1.0): ";
    for (int i = 0; i < 5; ++i) cout << realDist(gen) << " ";
    cout << endl;
    
    cout << "Normal distribution (μ=50, σ=15): ";
    for (int i = 0; i < 5; ++i) cout << static_cast<int>(normalDist(gen)) << " ";
    cout << endl;
    
    cout << "Coin flips: ";
    for (int i = 0; i < 10; ++i) cout << (coinFlip(gen) ? "H" : "T");
    cout << endl;
    
    // Shuffle with random generator
    vector<string> cards = {"A", "2", "3", "4", "5", "J", "Q", "K"};
    shuffle(cards.begin(), cards.end(), gen);
    cout << "Shuffled cards: ";
    for (const auto& card : cards) cout << card << " ";
    cout << endl;
    
    cout << "\n=== Modern C++ Utilities ===" << endl;
    
    // std::any (C++17) - type-safe container for any type
    any anyValue;
    anyValue = 42;
    cout << "any contains int: " << any_cast<int>(anyValue) << endl;
    
    anyValue = string("Hello World");
    cout << "any contains string: " << any_cast<string>(anyValue) << endl;
    
    // string_view (C++17) - non-owning string reference
    string_view processString(string_view sv) {
        cout << "Processing string_view: " << sv << " (length: " << sv.length() << ")" << endl;
        return sv.substr(0, 5);  // No copying!
    }
    
    string longString = "This is a very long string that we don't want to copy";
    auto firstPart = processString(longString);
    cout << "First part: " << firstPart << endl;
    
    // span (C++20) - view over contiguous sequence
    auto processArray = [](span<const int> arr) {
        cout << "Processing array of size " << arr.size() << ": ";
        for (const auto& elem : arr) cout << elem << " ";
        cout << endl;
    };
    
    int cArray[] = {1, 2, 3, 4, 5};
    vector<int> cppArray = {6, 7, 8, 9, 10};
    
    processArray(span<const int>(cArray, 5));
    processArray(span<const int>(cppArray));
    
    // Move semantics utilities
    cout << "\n=== Move and Perfect Forwarding ===" << endl;
    
    vector<string> source = {"apple", "banana", "cherry"};
    cout << "Source size before move: " << source.size() << endl;
    
    vector<string> destination = move(source);
    cout << "Source size after move: " << source.size() << endl;
    cout << "Destination size: " << destination.size() << endl;
    
    // Perfect forwarding example
    auto perfectForwarder = []<typename T>(T&& value) {
        return forward<T>(value);
    };
    
    string testStr = "Hello";
    auto forwarded1 = perfectForwarder(testStr);           // lvalue reference
    auto forwarded2 = perfectForwarder(move(testStr));     // rvalue reference
    
    cout << "Perfect forwarding preserves value categories" << endl;
    
    // std::exchange (C++14) - sets new value and returns old
    int oldValue = 10;
    int newValue = exchange(oldValue, 20);
    cout << "exchange: old=" << newValue << ", new=" << oldValue << endl;
    
    return 0;
}

Utility Best Practices:

  • Use smart pointers for automatic memory management - prefer make_unique/make_shared
  • std::optional is safer than nullable pointers for optional values
  • std::variant provides type-safe unions - better than C-style unions
  • Use chrono for all time-related operations - avoid platform-specific timing
  • Prefer structured bindings for accessing tuple/pair elements (C++17+)
  • string_view avoids unnecessary string copies - great for function parameters
  • Use type traits for compile-time type checking and SFINAE techniques