Control Flow, Functions and Lambdas · Lesson 9 of 33
Lambda Expressions in C++
A lambda is a small, unnamed function you write right where you need it – most often to tell an STL algorithm how to sort, filter or count. Lambdas are everywhere in modern C++, and they're essential in competitive programming (custom sort orders) and interviews.
In this lesson
- Lambda syntax
- Captures: by value, by reference, mixed
- Lambdas with STL algorithms
- Generic lambdas, mutable lambdas and std::function
The syntax
#include <iostream>
int main() {
auto square = [](int x) { return x * x; }; // [captures](params) { body }
auto add = [](int a, int b) -> long long { return 1LL * a + b; }; // explicit return type
auto hello = [] { std::cout << "Hello from a lambda\n"; }; // no parameters
std::cout << square(9) << ' ' << add(2'000'000'000, 2'000'000'000) << '\n';
hello();
std::cout << [](int n) { return n % 2 == 0; }(10) << '\n'; // defined and called at once
return 0;
}
81 4000000000 Hello from a lambda 1
Anatomy of a lambda
- [ ]capture list: which outside variables the lambda can use
- ( )parameters, like a normal function
- -> typeoptional return type (usually deduced)
- { }the body
Captures
#include <iostream>
int main() {
int threshold = 50;
int counter = 0;
auto above = [threshold](int x) { return x > threshold; }; // copy of threshold
auto count_call = [&counter] { counter++; }; // reference to counter
threshold = 90; // the lambda kept its own copy (50)
std::cout << above(70) << '\n';
count_call(); count_call();
std::cout << "counter = " << counter << '\n';
int a = 1, b = 2;
auto all_by_value = [=] { return a + b; }; // capture everything used, by value
auto all_by_ref = [&] { a *= 10; b *= 10; }; // capture everything used, by reference
all_by_ref();
std::cout << all_by_value() << ' ' << a + b << '\n'; // 3 (copies taken earlier), 30
return 0;
}
1 counter = 2 3 30
| Capture | Meaning |
|---|---|
[] | nothing |
[x] / [&x] | x by value (copy) / by reference |
[=] / [&] | everything used, by value / by reference |
[=, &total] | everything by value except total by reference |
[this] | the current object, inside member functions |
Dangling captures
A lambda that captures a local variable by reference and outlives it – for example one returned from a function – refers to a destroyed variable. Capture by value whenever the lambda may be stored or run later.
Lambdas with STL algorithms
#include <algorithm>
#include <iostream>
#include <numeric>
#include <string>
#include <vector>
struct Student { std::string name; int marks; };
int main() {
std::vector<int> v{5, 12, 7, 20, 3, 18};
std::sort(v.begin(), v.end(), [](int a, int b) { return a > b; }); // descending
for (int x : v) std::cout << x << ' ';
std::cout << '\n';
int limit = 10;
auto big = std::count_if(v.begin(), v.end(), [limit](int x) { return x > limit; });
auto first_odd = std::find_if(v.begin(), v.end(), [](int x) { return x % 2 != 0; });
std::cout << big << " above " << limit << ", first odd: " << *first_odd << '\n';
std::vector<Student> cls{{"Asha", 88}, {"Ravi", 74}, {"Meera", 88}, {"Kiran", 91}};
std::sort(cls.begin(), cls.end(), [](const Student& a, const Student& b) {
if (a.marks != b.marks) return a.marks > b.marks; // higher marks first
return a.name < b.name; // then by name
});
for (const auto& s : cls) std::cout << s.name << ' ' << s.marks << '\n';
int total = std::accumulate(cls.begin(), cls.end(), 0,
[](int sum, const Student& s) { return sum + s.marks; });
std::cout << "total " << total << '\n';
return 0;
}
20 18 12 7 5 3 3 above 10, first odd: 7 Kiran 91 Asha 88 Meera 88 Ravi 74 total 341
Comparator rule
A sort comparator must return true only when the first argument should come strictly before the second. Using >= or <= breaks that rule ("strict weak ordering") and can crash std::sort – a classic contest bug.
Generic lambdas, mutable and std::function
#include <functional>
#include <iostream>
#include <map>
#include <string>
int main() {
auto twice = [](auto x) { return x + x; }; // generic: works for any type with +
std::cout << twice(21) << ' ' << twice(1.5) << ' ' << twice(std::string{"ab"}) << '\n';
auto next_id = [id = 1000]() mutable { return ++id; }; // init-capture + mutable: keeps state
std::cout << next_id() << ' ' << next_id() << '\n';
std::map<char, std::function<int(int, int)>> ops{ // store different lambdas by type
{'+', [](int a, int b) { return a + b; }},
{'-', [](int a, int b) { return a - b; }},
{'*', [](int a, int b) { return a * b; }},
};
for (const auto& [symbol, op] : ops) std::cout << "12 " << symbol << " 4 = " << op(12, 4) << '\n';
return 0;
}
42 3 abab 1001 1002 12 * 4 = 48 12 + 4 = 16 12 - 4 = 8
Every lambda has its own unique type, so auto is the usual way to store one. std::function<R(Args...)> can hold any callable with that signature – useful in containers and callbacks, at a small runtime cost.
Practice
Try it yourself
Sort words by length, and alphabetically when lengths are equal.
Show solution
#include <algorithm> #include <iostream> #include <string> #include <vector> int main() { std::vector<std::string> w{"stack", "map", "set", "vector", "queue", "deque"}; std::sort(w.begin(), w.end(), [](const std::string& a, const std::string& b) { return a.size() != b.size() ? a.size() < b.size() : a < b; }); for (const auto& s : w) std::cout << s << ' '; std::cout << '\n'; return 0; }map set deque queue stack vector
Quick check
What does [&] capture?
Every outside variable the lambda uses, by reference.
Why does a by-value capture not see later changes?
It copied the value when the lambda was created.
What must a sort comparator return for equal elements?
false – it must be a strict "comes before" test.
Summary
- Lambdas are inline functions: [captures](params) { body }.
- Capture by value for safety, by reference to modify or avoid copies; watch dangling references.
- Pass lambdas to sort, count_if, find_if, accumulate; comparators must be strict.
Finished this lesson?
Mark it complete to track your progress, then carry on with the next lesson.