C++ Algorithms and Lambdas

Replace hand-written loops with standard algorithms such as sort, find_if, count_if, accumulate and transform, customised with lambda expressions.

Lesson 15 of 44Intermediate18 min read

Look at the loops in your programs and you'll see the same jobs again and again: find the first item that matches, count how many pass a test, add everything up, find the biggest, sort by some rule, remove the ones you don't want. Writing each of these by hand takes several lines, and every hand-written loop is a chance for an off-by-one mistake.

The C++ standard library has ready-made, well-tested versions of all of them in the <algorithm> and <numeric> headers. Each algorithm does one job, its name says what it does, and it works with vectors, arrays, strings and most other containers.

To tell an algorithm how to compare or which items to choose, you pass a small function, usually written on the spot as a lambda. In this lesson you'll learn how algorithms see your data through iterators, write lambdas with and without captures, and use the algorithms you'll need most.

Iterators: begin and end

Algorithms don't take a vector directly. They take a range, given as two iterators: one pointing at the first item and one pointing just past the last:

C++
vector<int> v = {5, 2, 9, 1};
sort(v.begin(), v.end());          // the whole vector
sort(v.begin(), v.begin() + 2);    // only the first two items

An iterator works like a pointer to an item: *it reads the item and it + 1 moves to the next. Algorithms that search return an iterator to what they found, or end() if nothing matched, so always compare the result with end() before using it. Get an item's index with it - v.begin().

Lambda expressions

A lambda is a function without a name, written right where it's needed:

C++
auto isEven = [](int n) { return n % 2 == 0; };
isEven(4);   // true
  • [] is the capture list: which outside variables the lambda can use.
  • (int n) are the parameters, and { ... } is the body.
  • [limit] captures limit by value (a copy); [&total] captures total by reference, so the lambda can change it.
  • [=] captures everything used by value and [&] everything by reference. Being explicit is clearer.

The algorithms you'll use most

AlgorithmDoes
sort(b, e) / sort(b, e, cmp)Sorts, optionally by your comparison
find(b, e, value) / find_if(b, e, pred)First match
count(b, e, value) / count_if(b, e, pred)How many match
any_of / all_of / none_ofTrue or false questions about the range
min_element / max_elementIterator to the smallest or largest
accumulate(b, e, start) (in <numeric>)Adds everything, starting from start
transform(b, e, out, f)Applies f to each item
reverse(b, e), binary_search(b, e, x)Reverses; searches a sorted range

A predicate is a function that returns bool, such as "is this price over 10?".

Removing items: remove_if and erase

remove_if doesn't actually shrink the vector. It moves the items you want to keep to the front and returns an iterator to the new logical end. You then erase everything after it:

C++
v.erase(remove_if(v.begin(), v.end(), [](int n) { return n < 0; }), v.end());

This "erase-remove idiom" appears in a lot of existing code. C++20 makes it a single call: erase_if(v, [](int n) { return n < 0; });.

C++ Algorithms Syntax

Algorithm patterns

#include <algorithm>
#include <numeric>
sort(v.begin(), v.end(), [](const T& a, const T& b) { return a.x < b.x; });
auto it = find_if(v.begin(), v.end(), [](const T& t) { return t.ok; });
if (it != v.end()) { /* found */ }
int n = count_if(v.begin(), v.end(), pred);
int sum = accumulate(v.begin(), v.end(), 0);
transform(v.begin(), v.end(), v.begin(), [](int x) { return x * 2; });
v.erase(remove_if(v.begin(), v.end(), pred), v.end());

C++ Algorithms Summary

  • Algorithms work on ranges given by iterators, usually v.begin() and v.end().
  • Search algorithms return end() when nothing is found; always check before using the result.
  • Lambdas [capture](params) { body } customise algorithms; capture by value or by reference.
  • sort, find_if, count_if, any_of, min_element, max_element, accumulate and transform cover most loops.
  • Remove items with the erase-remove idiom, or erase_if in C++20.

C++ Algorithms Examples

Each example shows the code, the output it produces and what each part does.

Example 1

Counting, summing and finding

C++
#include <iostream>
#include <vector>
#include <algorithm>
#include <numeric>
using namespace std;

int main() {
    vector<int> scores = {72, 45, 88, 91, 39, 64};

    int total = accumulate(scores.begin(), scores.end(), 0);
    int passed = count_if(scores.begin(), scores.end(), [](int s) { return s >= 50; });
    auto best = max_element(scores.begin(), scores.end());
    bool anyPerfect = any_of(scores.begin(), scores.end(), [](int s) { return s == 100; });

    cout << "Total: " << total << endl;
    cout << "Passed: " << passed << " of " << scores.size() << endl;
    cout << "Best: " << *best << " at index " << (best - scores.begin()) << endl;
    cout << "Any perfect score? " << (anyPerfect ? "yes" : "no") << endl;

    auto firstFail = find_if(scores.begin(), scores.end(), [](int s) { return s < 50; });
    if (firstFail != scores.end()) {
        cout << "First fail: " << *firstFail << endl;
    }
    return 0;
}
Output
Total: 399
Passed: 4 of 6
Best: 91 at index 3
Any perfect score? no
First fail: 45
Line-by-line explanation
  1. Line 10accumulate adds every score, starting from 0.
  2. Line 11count_if counts the items for which the lambda returns true.
  3. Line 12max_element returns an iterator; * reads the value and subtracting begin() gives the index.
  4. Lines 20 to 23Always check find_if's result against end() before reading it.
Example 2

Sorting objects with a lambda

C++
#include <iostream>
#include <string>
#include <vector>
#include <algorithm>
using namespace std;

struct Product {
    string name;
    double price;
    int stock;
};

int main() {
    vector<Product> shop = {
        {"Pen", 1.20, 300}, {"Backpack", 34.99, 15},
        {"Lamp", 24.50, 22}, {"Notebook", 3.50, 120}
    };

    sort(shop.begin(), shop.end(), [](const Product& a, const Product& b) {
        return a.price > b.price;
    });
    for (const auto& p : shop) {
        cout << p.name << " " << p.price << endl;
    }

    double budget = 10;
    int affordable = count_if(shop.begin(), shop.end(),
                              [budget](const Product& p) { return p.price <= budget; });
    cout << affordable << " products cost up to " << budget << endl;
    return 0;
}
Output
Backpack 34.99
Lamp 24.5
Notebook 3.5
Pen 1.2
2 products cost up to 10
Line-by-line explanation
  1. Lines 19 to 21The lambda says when a should come before b: here, when its price is higher.
  2. Lines 26 to 28[budget] captures the local variable so the lambda can use it. Each Product is passed by const reference to avoid copying it.
Example 3

Transforming and removing

C++
#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;

void print(const vector<int>& v) {
    for (int x : v) cout << x << " ";
    cout << endl;
}

int main() {
    vector<int> temps = {12, -3, 8, -1, 15, 0};

    int count = 0;
    for_each(temps.begin(), temps.end(), [&count](int t) { if (t < 0) count++; });
    cout << count << " readings below zero" << endl;

    temps.erase(remove_if(temps.begin(), temps.end(), [](int t) { return t < 0; }),
                temps.end());
    print(temps);

    transform(temps.begin(), temps.end(), temps.begin(), [](int c) { return c * 9 / 5 + 32; });
    print(temps);

    reverse(temps.begin(), temps.end());
    print(temps);
    return 0;
}
Output
2 readings below zero
12 8 15 0 
53 46 59 32 
32 59 46 53 
Line-by-line explanation
  1. Line 15[&count] captures by reference, so the lambda can change the outer variable.
  2. Lines 18 to 19remove_if moves the kept items forward; erase removes the leftovers at the end.
  3. Line 22transform writes each converted value back into the same vector. Integer maths makes 12 into 53, not 53.6.
  4. Line 25reverse flips the order in place.

Common C++ Algorithms Mistakes

Using a find result without checking

Don't
auto it = find(v.begin(), v.end(), 42);
cout << *it;                     // undefined if 42 isn't there
Do
auto it = find(v.begin(), v.end(), 42);
if (it != v.end()) cout << *it;

remove_if without erase

Don't
remove_if(v.begin(), v.end(), isNegative);    // size unchanged, junk at the end
Do
v.erase(remove_if(v.begin(), v.end(), isNegative), v.end());

accumulate with an int start for doubles

Don't
vector<double> prices = {1.5, 2.25};
double sum = accumulate(prices.begin(), prices.end(), 0);     // 3, decimals lost
Do
double sum = accumulate(prices.begin(), prices.end(), 0.0);   // 3.75

The type of the starting value decides the type of the total.

C++ Algorithms in Real Projects

Algorithms in real code

Game engines sort objects by distance before drawing them, trading systems use lambdas to filter and rank orders, and data tools count, transform and sum millions of values with these algorithms. Code reviews in professional C++ teams often suggest replacing a raw loop with the matching algorithm, because it's shorter, clearer and less likely to hide a bug.

C++ Algorithms FAQ

How do I sort a vector in descending order?

Pass a comparison that puts bigger items first: sort(v.begin(), v.end(), greater<int>()); with greater from <functional>, or a lambda [](int a, int b) { return a > b; }. Another trick is to sort with reverse iterators, sort(v.rbegin(), v.rend());, which fills the vector from the back.

How do I remove duplicates from a vector?

Sort it first so equal values sit next to each other, then use unique with erase: sort(v.begin(), v.end()); v.erase(unique(v.begin(), v.end()), v.end());. Like remove_if, unique only moves the kept items to the front and returns where the leftovers start, so the erase is essential. If the order must be kept, add each item to a set as you go and skip the ones already seen.

What is the difference between sort and stable_sort?

Both put the items in order, but stable_sort also keeps items that compare as equal in the order they started in, and sort doesn't promise that. It matters when you sort records in stages: sort students by name, then stable_sort by grade, and students with the same grade stay in name order. sort is usually a little faster.

What are ranges in C++20?

The <algorithm> header has std::ranges versions of most algorithms that take a whole container instead of a begin/end pair: ranges::sort(v); and ranges::find(v, 42). They're shorter and avoid mixing up iterators from different containers. C++20 also adds views for lazy pipelines such as filtering and transforming, which you'll meet in more advanced material.

How do I store a lambda in a variable?

Use auto: auto isBig = [](int x) { return x > 4; };, then call it as isBig(7) or pass it to an algorithm. Every lambda has its own unique type that only the compiler can name, which is why auto is needed. When a variable or parameter must hold different functions over time, use std::function<bool(int)> from <functional>, which is more flexible but slightly slower.

C++ Algorithms Exercises

Answer each one and check it instantly. If you get stuck, open the hint before the answer.

Fill in the blankseasy

Complete the call that sorts the whole vector.

sort(v.(), v.end());
Predict the outputeasy

What does this print?

vector<int> v = {3, 8, 1, 8};
cout << count(v.begin(), v.end(), 8) << endl;
Predict the outputmedium

What does this print?

vector<int> v = {4, 9, 2};
cout << accumulate(v.begin(), v.end(), 10) << endl;
Multiple choicemedium

What does [&total] mean in a lambda?

Multiple choicehard

Why does remove_if need to be followed by erase?

C++ Algorithms Quiz

5 questions to check what you learned.

  1. Which header contains sort and find?

    • <algorithm>
    • <vector>
    • <numeric>
    • <iterator>
  2. What does find return when the value isn't present?

    • -1
    • nullptr
    • The end() iterator
    • An exception
  3. What is a predicate?

    • A loop
    • A sorted range
    • A type of iterator
    • A function that returns true or false for an item
  4. In a sort comparison lambda (a, b), what should it return?

    • true if a should come before b
    • a minus b
    • The larger item
    • 0, 1 or -1
  5. Which C++20 function removes matching items from a vector in one call?

    • remove_all
    • erase_if
    • filter
    • delete_if