CppSTL Algorithms

STL Algorithms

The <algorithm> header is one of the most valuable parts of the C++ standard library, yet it is one of the most underused. Instead of hand-writing a raw for-loop every time you need to sort, search, count, or transform a sequence, the STL gives you a large library of algorithms that work uniformly across any container through iterators. Reaching for a standard algorithm instead of a loop you write yourself is almost always the better choice: it is more expressive (the algorithm name states the intent directly), it is less error-prone (off-by-one bugs in hand-rolled loops are extremely common), and it is often just as fast or faster than a naive loop you would write by hand.
Essential algorithms

Algorithm

Header

What it does

std::sort

<algorithm>

Sorts a range in place, ascending by default

std::find

<algorithm>

Returns an iterator to the first matching element, or end()

std::count

<algorithm>

Counts how many elements equal a value

std::count_if

<algorithm>

Counts how many elements satisfy a predicate

std::accumulate

<numeric>

Folds a range into a single value (e.g. sums it)

std::for_each

<algorithm>

Applies a function to every element

std::transform

<algorithm>

Writes a mapped version of a range into a destination

std::remove / erase-remove idiom

<algorithm>

Moves unwanted elements to the end (does not resize!)

std::max_element / std::min_element

<algorithm>

Returns an iterator to the largest/smallest element

std::reverse

<algorithm>

Reverses a range in place

A worked example

Here is a small pipeline that sorts a vector, searches it, and then transforms it, using nothing but standard algorithms — no manual loops written by hand.

Sort, find, then transform

CPP
#include <algorithm>
#include <iostream>
#include <numeric>
#include <vector>

int main() {
    std::vector<int> scores = {72, 95, 48, 61, 88};

    // 1. Sort ascending
    std::sort(scores.begin(), scores.end());

    // 2. Find the first score of at least 80
    auto it = std::find_if(scores.begin(), scores.end(),
                            [](int s) { return s >= 80; });
    if (it != scores.end()) {
        std::cout << "First score >= 80: " << *it << "\n";
    }

    // 3. Transform every score into a curved score (+5, capped at 100)
    std::vector<int> curved(scores.size());
    std::transform(scores.begin(), scores.end(), curved.begin(),
                    [](int s) { return std::min(100, s + 5); });

    // 4. Fold: compute the average of the curved scores
    int total = std::accumulate(curved.begin(), curved.end(), 0);
    double average = static_cast<double>(total) / curved.size();

    std::cout << "Average curved score: " << average << "\n";
    return 0;
}
The erase-remove idiom
std::remove (and std::remove_if) is one of the most misunderstood algorithms in the STL. Its name suggests it deletes elements from the container, but it does not — and it cannot, because algorithms only ever work through iterators and have no way to resize a container. What it actually does is shuffle the elements you want to keep toward the front of the range, and return an iterator marking the new logical end. The elements after that iterator are left in a valid but unspecified state — the container's size never changes.

remove() alone does not shrink the container

CPP
#include <algorithm>
#include <iostream>
#include <vector>

int main() {
    std::vector<int> nums = {1, 2, 3, 4, 5, 6};

    // remove() shuffles kept elements to the front and
    // returns an iterator marking the new logical end.
    auto newEnd = std::remove(nums.begin(), nums.end(), 3);

    std::cout << "Size after remove(): " << nums.size() << "\n"; // still 6!

    // erase() actually shrinks the container using that iterator.
    nums.erase(newEnd, nums.end());
    std::cout << "Size after erase(): " << nums.size() << "\n"; // now 5

    return 0;
}
std::remove does not resize the container
Calling std::remove (or std::remove_if) by itself leaves the container's size unchanged — it only rearranges elements. You must follow it with a call to the container's .erase() member function, passing the iterator that remove returned through to .end(), to actually shrink the container. This combination is universally known as the erase-remove idiom:

v.erase(std::remove(v.begin(), v.end(), value), v.end());
Prefer algorithms to hand-written loops
Beyond correctness, standard algorithms document their own intent. Seeing std::count_if(v.begin(), v.end(), pred) tells a reader immediately what the code does; a five-line hand-rolled loop with a counter variable requires reading every line to confirm the same thing. When you catch yourself writing a loop, pause and check whether <algorithm> or <numeric> already has a name for what you are doing.
  • <algorithm> and <numeric> provide reusable, well-tested building blocks for common operations on sequences.

  • Algorithms operate through iterators, so the same call works across vector, list, deque, and more.

  • std::remove/remove_if only rearrange elements; pair them with .erase() to actually shrink the container.

  • Reaching for a named algorithm communicates intent better than an equivalent hand-written loop.