Lambdas are how you customise algorithms, write callbacks and launch threads in modern C++.
A lambda is an anonymous function object written inline: [captures](params) { body }. The return type is deduced, or given as -> T.
The capture list says which outside variables the lambda can use:
[x]copiesxinto the lambda when it's created;[&x]refers to the original.[=]and[&]capture everything used, by copy or by reference. Prefer listing captures explicitly in longer code.- Init-captures create new members:
[n = 0], or[p = std::move(ptr)]to move a unique_ptr in. - In a member function,
[this]captures the object pointer and[*this]copies the object.
Captured copies are const inside the lambda unless you mark it mutable, which lets a lambda keep its own state between calls.
Each lambda has its own unique, unnamed type, so store one in auto. With auto parameters you get a generic lambda, a template in disguise; C++20 also allows an explicit template parameter list, []<typename T>(T x) { ... }. Lambdas with no captures convert to plain function pointers, handy for C APIs.
To store different callables in one variable or container, such as a list of callbacks, use std::function<int(int)> from <functional>. It type-erases any callable with that signature, at the cost of a possible heap allocation and an indirect call. Templates or auto parameters avoid that cost when you just pass a lambda to a function. C++23 adds std::move_only_function for callables that can't be copied.
The main danger: a lambda that captures by reference must not outlive the variables it captured. That bites with callbacks, threads and lambdas returned from functions; capture by value there.
Example
#include <algorithm>
#include <functional>
#include <iostream>
#include <memory>
#include <vector>
int main() {
std::vector<int> v{5, 2, 8, 1};
int threshold = 3;
auto above = [threshold](int x) { return x > threshold; }; // capture by value
std::cout << std::count_if(v.begin(), v.end(), above) << '\n'; // 2
int calls = 0;
std::sort(v.begin(), v.end(), [&calls](int a, int b) { ++calls; return a > b; });
std::cout << v[0] << ' ' << (calls > 0) << '\n'; // 8 1
auto add = [](auto a, auto b) { return a + b; }; // generic lambda
std::cout << add(1, 2) << ' ' << add(1.5, 2.0) << '\n'; // 3 3.5
auto counter = [n = 0]() mutable { return ++n; }; // own state
counter(); counter();
std::cout << counter() << '\n'; // 3
auto owned = [p = std::make_unique<int>(7)] { return *p; }; // move-only capture
std::function<int(int)> twice = [](int x) { return 2 * x; }; // type-erased
std::cout << owned() + twice(4) << '\n'; // 15
}Remember
[x]copies at creation,[&x]refers to the live variable- Use
mutableto modify by-value captures; init-captures[p = std::move(x)]move things in std::functionstores any callable but costs more thanauto/templates
Watch out for
- Capturing locals by reference in a callback or thread that runs after the function has returned.