Lambda Expressions
Basic Syntax
#include <iostream>
using namespace std;
int main() {
// [capture](parameters) -> returnType { body }
auto add = [](int a, int b) -> int {
return a + b;
};
cout << add(3, 4) << endl; // Output: 7
return 0;
}-> returnType part is optional — the compiler can usually deduce the return type automatically from the return statement(s) in the body, so it's often omitted for simple lambdas.auto multiply = [](int a, int b) {
return a * b; // return type deduced as int
};
cout << multiply(3, 4) << endl; // Output: 12Capture Modes
[...] at the start of a lambda is its capture list — it controls which variables from the surrounding scope the lambda body can access, and whether it accesses them by value or by reference.Capture | Meaning |
|---|---|
[] | Captures nothing; the lambda can only use its own parameters |
[=] | Captures all used outer variables by value (copies) |
[&] | Captures all used outer variables by reference |
[x] | Captures only x, by value |
[&x] | Captures only x, by reference |
int threshold = 10;
int count = 0;
// [threshold] - captured by value, a snapshot at creation time
auto isAboveThreshold = [threshold](int x) {
return x > threshold;
};
// [&count] - captured by reference, so this really modifies 'count'
auto tally = [&count](int x) {
count += x;
};
tally(5);
tally(3);
cout << count << endl; // Output: 8Lambdas with STL Algorithms
One of the most common uses for lambdas is providing a custom comparator or predicate to an STL algorithm on the spot, instead of writing a separate named function just for that one call.
#include <algorithm>
#include <vector>
#include <iostream>
using namespace std;
int main() {
vector<int> nums = {5, 2, 8, 1, 9, 3};
// Sort in descending order using a lambda as the comparator
sort(nums.begin(), nums.end(), [](int a, int b) {
return a > b;
});
for (int n : nums) {
cout << n << " ";
}
cout << endl;
// Output: 9 8 5 3 2 1
return 0;
}#include <algorithm>
#include <vector>
#include <iostream>
using namespace std;
int main() {
vector<int> nums = {1, 2, 3, 4, 5, 6, 7, 8};
// Count how many elements are even, using a lambda predicate
int evenCount = count_if(nums.begin(), nums.end(), [](int n) {
return n % 2 == 0;
});
cout << "Even numbers: " << evenCount << endl; // Output: Even numbers: 4
return 0;
}Storing Lambdas: auto and std::function
auto is the simplest way to store one in a variable. If you need a variable that can hold different callables (a lambda, a function pointer, a functor) at different times, or you need to store it as a class member or pass it across a function boundary with a fixed type, wrap it in std::function.#include <functional>
#include <iostream>
using namespace std;
int main() {
auto square = [](int x) { return x * x; }; // simplest: auto
std::function<int(int)> op = square; // std::function: a fixed, storable type
op = [](int x) { return x + 1; }; // can be reassigned to a different lambda
cout << square(5) << endl; // Output: 25
cout << op(5) << endl; // Output: 6
return 0;
}auto for local variables holding a single lambda — it's zero-overhead. Reach for std::function when you need type erasure, such as a class member, a container of callbacks, or a function parameter that should accept any callable.Key Points
Lambda syntax: capture -> returnType { body }; the return type is usually deducible and omitted.
[] captures nothing, [=] captures by value, [&] captures by reference; specific variables can be listed individually.
Lambdas are widely used as inline comparators/predicates for STL algorithms like sort and count_if.
auto is the simplest way to store a lambda; std::function is used when a uniform, storable callable type is needed.
Values captured by [=] are snapshotted at creation time, not re-read on each call.