Modern C++ Fundamentals: From Basics to Smart Pointers and Threads · Modern C++ Basics: Types, Containers, Functions and Classes

Functions, Lambdas and the Standard Algorithms

Overload functions, use default arguments, write lambdas with captures, and replace hand-written loops with std::sort, std::find_if, std::transform and std::accumulate.

Create a free account to save this page, take notes and track your progress. Sign in

What you'll build

A product-catalog tool that filters, sorts, totals and transforms a list of products using lambdas and standard algorithms instead of manual loops.

The idea

  • Overloading: several functions with the same name and different parameter types. The compiler picks the best match.
  • Default arguments: void log(std::string_view msg, int level = 1).
  • Lambdas are small anonymous functions: [captures](parameters) { body }. A capture list decides which outside variables the lambda can use: [] none, [=] copies, [&] references, [threshold] one by value, [&total] one by reference.
  • std::function<R(Args...)> can hold any callable with that signature; use it to store callbacks. For parameters in templates, a plain template parameter is faster.
  • Standard algorithms (<algorithm>, <numeric>) express intent: std::sort, std::find_if, std::count_if, std::transform, std::remove_if, std::accumulate. They work on iterator pairs, such as v.begin(), v.end().
  • Erase-remove: std::erase_if(v, pred) (C++20) removes matching elements from a vector in one call.

Prefer an algorithm with a clear name over a loop that someone must read to understand.

The code

Lambdas with captures and standard algorithms
#include <algorithm>
#include <iostream>
#include <numeric>
#include <string>
#include <vector>

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

// Overloads: same name, different parameters
std::string describe(const Product& p) { return p.name + " ($" + std::to_string(static_cast<int>(p.price)) + ")"; }
std::string describe(const std::vector<Product>& items) {
    std::string out;
    for (const auto& p : items) out += describe(p) + " ";
    return out;
}

// Default argument
void print_header(const std::string& title, int width = 24) {
    std::cout << std::string(static_cast<size_t>(width), '-') << '\n' << title << '\n';
}

int main() {
    std::vector<Product> catalog{
        {"Keyboard", 49.0, 12}, {"Mouse", 19.0, 0}, {"Monitor", 189.0, 4}, {"Cable", 5.0, 100}, {"Webcam", 59.0, 0},
    };

    print_header("In stock, cheapest first");
    std::vector<Product> in_stock = catalog;
    std::erase_if(in_stock, [](const Product& p) { return p.stock == 0; });
    std::sort(in_stock.begin(), in_stock.end(), [](const Product& a, const Product& b) { return a.price < b.price; });
    std::cout << describe(in_stock) << '\n';

    // Captures: use a local variable inside the lambda
    const double budget = 60.0;
    auto affordable = std::count_if(catalog.begin(), catalog.end(),
                                    [budget](const Product& p) { return p.price <= budget; });
    std::cout << affordable << " products cost at most " << budget << '\n';

    // find_if returns an iterator; compare with end() before dereferencing
    auto it = std::find_if(catalog.begin(), catalog.end(), [](const Product& p) { return p.price > 100; });
    if (it != catalog.end()) std::cout << "first over $100: " << it->name << '\n';

    // accumulate totals with a custom operation
    const double stock_value = std::accumulate(catalog.begin(), catalog.end(), 0.0,
                                               [](double sum, const Product& p) { return sum + p.price * p.stock; });
    std::cout << "stock value: " << stock_value << '\n';

    // transform into a new vector
    std::vector<std::string> names;
    std::transform(catalog.begin(), catalog.end(), std::back_inserter(names), [](const Product& p) { return p.name; });
    std::cout << names.size() << " names, last: " << names.back() << '\n';

    // A mutable counter captured by reference
    int visited = 0;
    std::for_each(catalog.begin(), catalog.end(), [&visited](const Product&) { ++visited; });
    std::cout << "visited " << visited << '\n';
}

Output

------------------------
In stock, cheapest first
Cable ($5) Keyboard ($49) Monitor ($189) 
4 products cost at most 60
first over $100: Monitor
stock value: 1844
5 names, last: Webcam
visited 5

Run this on your computer — see “Get Started” for setup.

Common mistakes

  • Capturing by reference a variable that outlives its scope. A lambda stored or returned after its captured locals are gone has dangling references; capture by value.
  • Using [=] and [&] carelessly. Prefer explicit captures so readers see what a lambda depends on.
  • Dereferencing find_if results without checking != end().
  • Calling std::remove_if and forgetting to erase. remove_if only moves elements; std::erase_if (C++20) or the erase-remove idiom actually shrinks the container.
  • Passing a lambda as std::function in hot loops. It may allocate and block inlining; use a template parameter or auto for performance-sensitive code.

Key points

  • Overload functions by parameter type; use default arguments for optional values.
  • Lambdas are [captures](params) { body }; list captures explicitly.
  • Standard algorithms (sort, find_if, count_if, transform, accumulate, erase_if) state intent more clearly than loops.
  • Always compare iterators from find to end() before dereferencing.

Further reading: cppreference: Algorithms library.

Practice questions

What is the difference between capturing with `[=]`, `[&]` and `[x]`?

Answer

[=] copies every outside variable the lambda uses, [&] refers to them by reference, and [x] copies only x. Explicit captures like [x] or [&total] make dependencies visible and avoid accidental sharing or dangling references.

Why doesn't std::remove_if alone shrink a vector?

Answer

It only moves the elements to keep to the front and returns the new logical end; the container's size is unchanged. You must call erase on the leftover range, or use std::erase_if (C++20) which does both.

Open in the app to mark lessons complete, take notes and track your progress.

Was this page helpful?