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
#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"); }
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:
#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"; }
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:
| Capture | Meaning |
|---|---|
[] | 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 |
#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"; }
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:
#include <iostream> int main() { auto next_id = [counter = 0]() mutable { return ++counter; }; std::cout << next_id() << ' ' << next_id() << ' ' << next_id() << '\n'; }
1 2 3
Generic lambdas
With auto parameters, a lambda works for any type — it's a small template (lesson 39):
#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'; }
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:
#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'; } }
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:
#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'; }
Good afternoon
Try it yourself
Sort words by length, then alphabetically for equal lengths, using a lambda comparison.
fig kiwi plum apple banana cherry