Lambda Expressions in C++
In this lesson: Write lambda expressions in C++ — syntax, [=] / [&] capture and use with sort and for_each — with output. In Hindi & English.
What is a lambda?
A lambda expression is a small, unnamed function written right where you need it. Before C++11, sorting in descending order meant writing a separate named function just to pass it to sort. With a lambda you write the rule inline:
std::sort(marks.begin(), marks.end(), [](int a, int b) { return a > b; });Lambdas make code shorter and keep the logic next to where it is used. They are everywhere in modern C++: STL algorithms, callbacks, threads and event handlers.
Syntax
[capture] (parameters) mutable -> return_type { body }| Part | Meaning | Required? |
|---|---|---|
[capture] | Which outside variables the lambda can use | Yes (can be empty []) |
(parameters) | Inputs, like a normal function | Optional if none |
mutable | Allow changing by-value captures | Optional |
-> return_type | Explicit return type | Optional — usually deduced |
{ body } | The code | Yes |
First examples
#include <iostream>
int main() {
auto greet = []() { std::cout << "Hello from a lambda!\n"; };
greet();
auto add = [](int a, int b) { return a + b; };
std::cout << "3 + 4 = " << add(3, 4) << "\n";
auto average = [](double total, int count) -> double {
return count == 0 ? 0.0 : total / count;
};
std::cout << "average = " << average(254, 3) << "\n";
// call immediately
std::cout << [](int x) { return x * x; }(9) << "\n";
return 0;
}auto is used because every lambda has its own unique, unnamed type.
The capture list
A lambda can only use outside local variables that it captures.
| Capture | Meaning |
|---|---|
[] | Capture nothing |
[x] | Copy x (by value) |
[&x] | Refer to the original x (by reference) |
[=] | Copy every outside variable used in the body |
[&] | Refer to every outside variable used in the body |
[=, &total] | Copy all, but total by reference |
[this] | Use members of the current object (inside a class) |
#include <iostream>
int main() {
int passMark = 33;
int count = 0;
auto isPass = [passMark](int m) { return m >= passMark; }; // copy
auto countOne = [&count]() { count++; }; // reference
passMark = 40; // lambda still uses its copy: 33
std::cout << isPass(35) << "\n"; // 1 (35 >= 33)
countOne();
countOne();
std::cout << "count = " << count << "\n"; // 2
return 0;
}mutable lambdas
By-value captures are read-only inside the lambda. mutable lets the lambda change *its own copy*:
int n = 0;
auto next = [n]() mutable { return ++n; };
std::cout << next() << next() << next(); // 123
std::cout << " n = " << n; // n = 0 (original unchanged)Lambdas with STL algorithms
This is where lambdas shine.
#include <iostream>
#include <vector>
#include <algorithm>
#include <string>
struct Student { std::string name; int marks; };
int main() {
std::vector<Student> cls = {{"Aman", 78}, {"Priya", 91}, {"Ravi", 29}, {"Sara", 66}};
// sort by marks, highest first
std::sort(cls.begin(), cls.end(),
[](const Student& a, const Student& b) { return a.marks > b.marks; });
// count passes
int passMark = 33;
int passed = std::count_if(cls.begin(), cls.end(),
[passMark](const Student& s) { return s.marks >= passMark; });
// print each
std::for_each(cls.begin(), cls.end(),
[](const Student& s) { std::cout << s.name << " " << s.marks << "\n"; });
std::cout << "Passed: " << passed << " of " << cls.size() << "\n";
// find first student below 70
auto it = std::find_if(cls.begin(), cls.end(),
[](const Student& s) { return s.marks < 70; });
if (it != cls.end()) std::cout << "First below 70: " << it->name << "\n";
return 0;
}Generic lambdas (C++14)
Use auto for parameters and the lambda works with any type:
auto maxOf = [](auto a, auto b) { return a > b ? a : b; };
std::cout << maxOf(3, 7) << " " << maxOf(2.5, 1.5) << " "
<< maxOf(std::string("abc"), std::string("abd")); // 7 2.5 abdStoring lambdas: auto vs std::function
auto f = [](int x) { ... };— the fastest; use this normally.std::function<int(int)> f = ...;(header<functional>) — needed when you must store different lambdas in the same variable or container, e.g. a menu of operations.
#include <functional>
#include <map>
std::map<char, std::function<int(int, int)>> ops = {
{'+', [](int a, int b) { return a + b; }},
{'-', [](int a, int b) { return a - b; }},
};
std::cout << ops['+'](8, 5); // 13Common mistakes
- Using an outside variable without capturing it — compile error "is not captured".
- Capturing a local by reference in a lambda that runs after the function has returned (e.g. stored and called later) — dangling reference.
- Expecting
[x]to see later changes tox. - Forgetting
mutablewhen changing a by-value capture — "assignment of read-only variable". - Writing a 40-line lambda; if it is that long, make it a named function.
Store 10 marks in a vector<int>. With lambdas: sort them in descending order, count how many are between 60 and 80 (capture the limits by value), add 5 grace marks to every mark below 33 using for_each with a reference parameter, and print the result.
Summary
- A lambda is an inline, unnamed function:
[capture](params) { body }. - Captures:
[x]copy,[&x]reference,[=]all by copy,[&]all by reference. mutablelets a lambda change its by-value copies.- Lambdas are ideal comparators and predicates for
sort,count_if,find_if,for_each. - Store with
auto; usestd::functionwhen different lambdas share a variable.
Related: functions, STL algorithms, modern C++ features, sorting and searching.
Frequently Asked Questions
What is a lambda expression in C++?
[](int a, int b) { return a > b; }. It was added in C++11 and is most often passed to STL algorithms such as sort, count_if and for_each.What is the capture list in a C++ lambda?
[ ], decides which variables from the surrounding scope the lambda can use. [x] copies x, [&x] refers to the original x, [=] copies everything used, and [&] refers to everything used. [] captures nothing.What is the difference between capture by value and capture by reference?
[x]) stores a copy made when the lambda is created, so later changes to x are not seen and the lambda cannot modify the original. Capture by reference ([&x]) uses the original variable, so it sees changes and can modify it — but the variable must still exist when the lambda runs.Why is mutable used in a lambda?
mutable after the parameter list lets the lambda modify its own copies, for example a counter captured by value. It does not change the original variable outside.