C++ Lambdas and Closures for Dummies
1. What is a Lambda (Anonymous Function)?
- A lambda function in C++ is an anonymous function you can define inline.
- Syntax:
[capture] (parameters) -> return_type { body }
- The capture list (
[capture]
) determines what variables from the surrounding scope the lambda has access to.
- The capture list (
Example: Basic Lambda
#include <iostream>
int main() {
auto add = [](int x, int y) -> int { return x + y; };
std::cout << add(3, 5) << std::endl; // Output: 8
}
Simplified Lambda (Return Type Inferred)
auto add = [](int x, int y) { return x + y; }; // No need to specify return type
std::cout << add(3, 5) << std::endl;
2. Capturing Variables in a Lambda (Closures)
- A closure is when a lambda function remembers variables from its enclosing scope.
- You control what the lambda captures using the capture list inside
[]
.
Capture Syntax Overview
Capture Syntax | Meaning |
---|---|
[=] |
Capture all variables by value |
[&] |
Capture all variables by reference |
[x] |
Capture x by value |
[&x] |
Capture x by reference |
[x, &y] |
Capture x by value, y by reference |
Example: Capturing by Value (``)
#include <iostream>
int main() {
int a = 10, b = 20;
auto sum = [=]() { return a + b; }; // Capture a and b by value
std::cout << sum() << std::endl; // Output: 30
}
✅ What happens here?
a
andb
are copied into the lambda.- The lambda does not modify the original
a
andb
.
Example: Capturing by Reference (``)
#include <iostream>
int main() {
int count = 0;
auto increment = [&]() { return ++count; }; // Capture by reference
std::cout << increment() << std::endl; // 1
std::cout << increment() << std::endl; // 2
}
✅ What happens here?
count
is captured by reference, so changes affect the original variable.- Every call to
increment()
updates `` outside the lambda.
Example: Capturing Specific Variables
#include <iostream>
int main() {
int x = 5, y = 10;
auto lambda = [x, &y]() { return x + (++y); }; // x by value, y by reference
std::cout << lambda() << std::endl; // y is modified
std::cout << y << std::endl; // Shows the updated y
}
✅ What happens here?
x
is captured by value (remains unchanged in the outer scope).y
is captured by reference (changes persist outside the lambda).
3. Closures in Action: Counter Example
#include <iostream>
#include <functional>
std::function<int()> makeCounter(int start) {
int count = start;
return [count]() mutable { return ++count; }; // Capture count and modify it
}
int main() {
auto counter1 = makeCounter(10);
std::cout << counter1() << std::endl; // 11
std::cout << counter1() << std::endl; // 12
auto counter2 = makeCounter(50);
std::cout << counter2() << std::endl; // 51 (Separate instance)
}
✅ What happens here?
makeCounter(10)
returns a function that remembers ****``.- Each counter is independent because
count
is captured by value but markedmutable
.
4. When to Use Lambdas vs. Regular Functions
Feature | Use Lambda | Use Regular Function |
---|---|---|
Short function (one-liner) | ✅ Yes | ❌ No |
Needs to capture state from scope | ✅ Yes | ❌ No |
Can be used in std::sort() , std::for_each() |
✅ Yes | ❌ No |
Used multiple times in the code | ❌ No | ✅ Yes |
Complex function with multiple lines | ❌ No | ✅ Yes |
🚀 Takeaway
- ✅ Lambdas are great for quick, inline functions.
- ✅ Closures allow lambdas to remember values after execution.
- ✅ Use closures when you need multiple independent function instances.