Lambda Expressions

A lambda is a function you write inline, right where you need it — usually to pass to an algorithm: "sort by price", "keep the even numbers", "do this for each item". Lambdas can also capture variables from the surrounding code.

A first lambda

C++
#include <iostream>

int main() {
    auto square = [](int x) { return x * x; };
    auto greet = [](const char* name) { std::cout << "Hello, " << name << "!\n"; };

    std::cout << square(7) << '\n';
    greet("Ada");
}
Output
49
Hello, Ada!

The parts: [] is the capture list, (int x) the parameters, and { … } the body. The return type is deduced; you can state it with -> type if needed. Each lambda has its own unique type, so store it with auto.

Lambdas with algorithms

This is where lambdas shine. The standard algorithms take a function that decides how to compare, test or transform elements:

C++
#include <algorithm>
#include <iostream>
#include <string>
#include <vector>

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

int main() {
    std::vector<Product> items{{"Pen", 1.20}, {"Backpack", 39.90}, {"Notebook", 3.50}};

    std::sort(items.begin(), items.end(),
              [](const Product& a, const Product& b) { return a.price < b.price; });
    for (const auto& p : items) std::cout << p.name << ' ';
    std::cout << '\n';

    auto cheap = std::count_if(items.begin(), items.end(), [](const Product& p) { return p.price < 5; });
    std::cout << cheap << " items under 5\n";
}
Output
Pen Notebook Backpack
2 items under 5

Capturing variables

A lambda can use variables from where it's written by listing them in the capture list:

CaptureMeaning
[]Capture nothing
[limit]Copy limit into the lambda
[&total]Refer to total (changes are visible outside)
[=] / [&]Capture everything used, by copy / by reference
[this]Access the current object's members (inside a class)
[n = x * 2]Initialise a new captured variable
C++
#include <algorithm>
#include <iostream>
#include <vector>

int main() {
    std::vector<int> values{4, 17, 9, 23, 1, 12};
    int limit = 10;
    int total = 0;

    std::for_each(values.begin(), values.end(), [limit, &total](int v) {
        if (v > limit) total += v;
    });
    std::cout << "sum of values above " << limit << ": " << total << '\n';

    auto above = std::count_if(values.begin(), values.end(), [=](int v) { return v > limit; });
    std::cout << above << " values above the limit\n";
}
Output
sum of values above 10: 52
3 values above the limit

Capturing by copy takes a snapshot when the lambda is created; capturing by reference sees later changes — and becomes dangling if the variable dies before the lambda is called. Prefer explicit captures over [&] or [=] in longer code, so it's clear what the lambda depends on.

Mutable lambdas

Variables captured by copy are read-only inside the lambda unless it's marked mutable. That lets a lambda keep its own private state:

C++
#include <iostream>

int main() {
    auto next_id = [counter = 0]() mutable { return ++counter; };
    std::cout << next_id() << ' ' << next_id() << ' ' << next_id() << '\n';
}
Output
1 2 3

Generic lambdas

With auto parameters, a lambda works for any type — it's a small template (lesson 39):

C++
#include <iostream>
#include <string>

int main() {
    auto twice = [](const auto& x) { return x + x; };
    std::cout << twice(21) << ' ' << twice(1.25) << ' ' << twice(std::string{"ab"}) << '\n';
}
Output
42 2.5 abab

std::function

To store "any callable with this signature" — a lambda, a function or a function object — use std::function. It's handy for callbacks and tables of actions, at a small run-time cost:

C++
#include <functional>
#include <iostream>
#include <map>
#include <string>

int main() {
    std::map<std::string, std::function<double(double, double)>> ops{
        {"+", [](double a, double b) { return a + b; }},
        {"*", [](double a, double b) { return a * b; }},
        {"max", [](double a, double b) { return a > b ? a : b; }},
    };
    for (const auto& [name, op] : ops) {
        std::cout << "6 " << name << " 7 = " << op(6, 7) << '\n';
    }
}
Output
6 * 7 = 42
6 + 7 = 13
6 max 7 = 7

When the callable is only passed straight to a function, prefer a template parameter or auto over std::function; it avoids the overhead.

Immediately invoked lambdas

A lambda called on the spot can initialise a const variable that needs several steps to compute:

C++
#include <iostream>
#include <string>

int main() {
    int hour = 14;
    const std::string greeting = [&] {
        if (hour < 12) return "Good morning";
        if (hour < 18) return "Good afternoon";
        return "Good evening";
    }();
    std::cout << greeting << '\n';
}
Output
Good afternoon

Try it yourself

Sort words by length, then alphabetically for equal lengths, using a lambda comparison.

Try it yourself · main.cpp
Edit the code, copy it into main.cpp, then compile and run it with c++ -std=c++20 -Wall main.cpp -o main && ./main. How to set up a C++ compiler
Output
fig kiwi plum apple banana cherry