C++ Standard Library · Lesson 8

Algorithms: the Workhorses

~100 ready-made functions in <algorithm> and <numeric> that replace hand-written loops. Learn ~15 and you'll reach for them instead of writing for by hand.

🎯 Express intent — sort/find/transform without reinventing loops
The one idea

Most loops you'd write by hand already exist as a named, tested, optimized algorithm. sort, find, transform, accumulate, count_if — they take a range plus (often) a small function, and say what you mean instead of how to loop.

01 The shape of every algorithm

Almost all of them follow one pattern: a range (begin, end), then optionally a value to look for or a callable (a predicate or operation — usually a lambda, Lesson 9). They return an iterator, a count, or write into a destination.

#include <algorithm>

std::vector<int> v = {5, 3, 1, 4, 2};

std::sort(v.begin(), v.end());                    // {1,2,3,4,5}
auto it = std::find(v.begin(), v.end(), 3);       // iterator to the 3
int n  = std::count_if(v.begin(), v.end(),
                       [](int x){ return x > 2; });   // 3 elements > 2
Run on godbolt.org (-std=c++17+). C++20? Swap in std::ranges::sort(v) & friends.
💡 Why bother over a hand loop? It's named (a reader sees "this sorts" instantly), correct (no off-by-one), and often faster than the obvious loop. The Core Guidelines put it first: prefer library algorithms to raw loops.

Source: Core Guidelines P.1/SL — express intent; prefer algorithms.

02 The essential set

GroupAlgorithmDoes
Searchfind / find_iffirst element equal to / matching a predicate
count / count_ifhow many equal / match
any_of / all_of / none_ofdoes any / every / no element match?
binary_search / lower_boundfast lookup in a sorted range (O(log n))
Ordersort / stable_sortsort (stable keeps equal-elements' order)
nth_element / partial_sortpartial ordering (e.g. top-K) without full sort
min_element / max_elementiterator to the smallest / largest
Modifytransformmap each element through a function into a destination
copy / fill / replacecopy a range / set all to a value / swap a value
remove_if + erase / uniquedrop matching / collapse adjacent duplicates (§04)
Numeric
<numeric>
accumulate / reducefold a range to one value (sum, product…)
iotafill with increasing values (0,1,2,…)
⚠ <numeric> is a separate header accumulate, reduce, iota, and inner_product live in <numeric>, not <algorithm>. Forgetting the include is a common "why won't it compile" moment.

Source: cppreference — Algorithms · Numeric. Full quick list in the cheat sheet.

03 Worked examples

Sort with a custom rule

// sort descending — pass a comparator lambda (returns "a before b?")
std::sort(v.begin(), v.end(), [](int a, int b){ return a > b; });

// sort people by age
std::sort(people.begin(), people.end(),
          [](const Person& a, const Person& b){ return a.age < b.age; });

Sum and product with accumulate

#include <numeric>
int sum  = std::accumulate(v.begin(), v.end(), 0);          // 0 + each → total
int prod = std::accumulate(v.begin(), v.end(), 1,
                            std::multiplies<>());           // 1 * each → product
💡 The "init" argument matters That third argument to accumulate is the starting value and sets the result type. accumulate(v.begin(), v.end(), 0) accumulates in int; for doubles use 0.0, or you'll silently truncate.

Map a range into a new one

auto lengths = std::vector<size_t>{};
std::transform(words.begin(), words.end(),
               std::back_inserter(lengths),
               [](const std::string& w){ return w.size(); });
// lengths now holds the length of each word; back_inserter push_backs results

Source: cppreference — accumulate, transform.

04 The erase-remove idiom (a rite of passage)

This one trips up everyone once. std::remove_if does not remove anything — it can't, because an algorithm only has iterators, not the container. It shuffles the "kept" elements to the front and returns an iterator to the new logical end; the container's size() is unchanged until you actually erase the tail.

// PRE-C++20: the classic two-step "erase-remove" idiom
v.erase(std::remove_if(v.begin(), v.end(),
                       [](int x){ return x % 2 == 0; }),   // move evens to back
        v.end());                                            // erase them

// C++20: one line does both
std::erase_if(v, [](int x){ return x % 2 == 0; });
⚠ remove_if alone is a no-op-looking bug Calling std::remove_if(...) and ignoring the return leaves the container the same size with garbage at the end. Always pair it with erase — or use C++20 std::erase_if / std::erase, which exist precisely to kill this footgun.

Source: cppreference — remove/remove_if & std::erase / erase_if.

05 Check yourself

What's the main reason to prefer std::find / std::sort over a hand-written loop?
After std::remove_if(v.begin(), v.end(), pred) with no erase, what is v.size()?
std::accumulate(v.begin(), v.end(), 0) on a vector of doubles loses the fractional parts. Why?
Which header holds accumulate and iota?

06 Flashcards

Q: What's the common shape of a standard algorithm call?
A: A range (begin, end), then optionally a value to match or a callable (predicate/operation). Returns an iterator, count, or writes to a destination.
Q: Name the "must-know" handful.
A: sort, find/find_if, count_if, any_of/all_of, transform, accumulate, min_element/max_element, remove_if+erase.
Q: What does remove_if actually do?
A: It moves kept elements to the front and returns an iterator to the new logical end. It does not shrink the container — pair it with erase, or use C++20 std::erase_if.
Q: How do you sort by a custom rule?
A: Pass a comparator as the third arg: sort(b, e, [](auto& a, auto& b){ return a < b; }) — return true if a should come before b.
Q: Where do numeric folds live?
A: In <numeric> (not <algorithm>): accumulate, reduce, iota, inner_product. The init value sets the result type.
Q: How does the ranges form differ?
A: std::ranges::sort(v) / std::ranges::find(v, x) take the container directly — same algorithms, no begin/end (C++20).
👩‍🏫 I'm your teacher — ask me anything

Best drill: paste me a hand-written loop and ask "which algorithm replaces this?" Or try "top 3 scores without fully sorting" (hint: partial_sort/nth_element). Next: the lambdas these algorithms eat.