Skip to content

Lambda_expressions

Lambdas provide a way to define anonymous function objects inline. They’re extremely useful with STL algorithms and as callbacks.

[ captures ] ( parameters ) -> return_type { body }
  • captures: Variables from surrounding scope
  • parameters: Function parameters (can be empty)
  • return_type: Return type (often auto-deduced)
  • body: Function body
// No parameters, no captures
auto hello = []() { std::cout << "Hello!\n"; };
hello(); // "Hello!"
// With parameters
auto add = [](int a, int b) { return a + b; };
int result = add(5, 3); // 8
// With return type
auto divide = [](double a, double b) -> double {
return a / b;
};

Captures allow lambdas to access variables from the surrounding scope:

int x = 10;
auto lambda = [x]() { return x; }; // Copies x
int x = 10;
auto lambda = [&x]() { x = 20; }; // References x
int a = 1, b = 2;
// Capture all by value
auto f1 = [=]() { return a + b; };
// Capture all by reference
auto f2 = [&]() { return a + b; };
// Mixed: a by value, b by reference
auto f3 = [a, &b]() { b = 20; return a; };
int count = 0;
auto counter = [count]() mutable {
count++; // Can modify captured copy
return count;
};
counter(); // 1
counter(); // 2 (count is now 2)
auto add = [](auto a, auto b) { return a + b; };
int i = add(1, 2); // 3
double d = add(1.5, 2.5); // 4.0
std::string s = add("Hello, ", "World!"); // "Hello, World!"
#include <algorithm>
#include <vector>
std::vector<int> vec = {5, 2, 8, 1, 9, 3};
// Find first element > 5
auto it = std::find_if(vec.begin(), vec.end(),
[](int n) { return n > 5; });
// Sort with custom comparator
std::sort(vec.begin(), vec.end(),
[](int a, int b) { return a > b; });
// Count elements
int count = std::count_if(vec.begin(), vec.end(),
[](int n) { return n % 2 == 0; });
// Transform
std::vector<int> result;
std::transform(vec.begin(), vec.end(), std::back_inserter(result),
[](int n) { return n * 2; });
std::set<int, std::function<bool(int, int)>> customSet(
[](int a, int b) { return a > b; } // Descending order
);
void processData(int value, std::function<void(int)> callback) {
// Process...
callback(value * 2);
}
int main() {
processData(5, [](int result) {
std::cout << "Result: " << result << "\n";
});
}
#include <iostream>
#include <vector>
#include <functional>
class EventManager {
private:
std::vector<std::function<void()>> listeners;
public:
void subscribe(std::function<void()> callback) {
listeners.push_back(callback);
}
void trigger() {
for (auto& listener : listeners) {
listener();
}
}
};
int main() {
EventManager events;
int notificationCount = 0;
// Subscribe with lambda (captures notificationCount by reference)
events.subscribe([&notificationCount]() {
notificationCount++;
std::cout << "Event triggered!\n";
});
events.subscribe([&notificationCount]() {
std::cout << "Second listener. Total: " << notificationCount << "\n";
});
std::cout << "=== Trigger 1 ===\n";
events.trigger();
std::cout << "=== Trigger 2 ===\n";
events.trigger();
return 0;
}
  • Lambda: Anonymous function object, lightweight
  • std::function: Type-erased wrapper, can store any callable
// Lambda (direct type)
auto lambda = [](int x) { return x * 2; };
// std::function (type-erased)
std::function<int(int)> func = lambda;
  1. Use captures wisely - prefer const& for read-only
for (const auto& item : items) {
process([&item]() { /* use item */ });
}
  1. Avoid unnecessary captures - only capture what’s needed
// Good
auto f = [value]() { return value * 2; };
// Avoid (unnecessary capture)
int unused = 0;
auto f = [unused, value]() { return value * 2; };
  1. Use generic lambdas (C++14) for flexible code
  • Lambdas create anonymous function objects inline
  • Captures access variables from surrounding scope
  • Use [=] for copy, [&] for reference
  • Lambdas work great with STL algorithms
  • Generic lambdas (C++14) work with any type

Let’s learn about move semantics - a powerful feature that enables efficient resource transfer.

Next Chapter: 26_move_semantics.md - Move Semantics and Rvalue References