Lambda-Funktionen und funktionaler Stil in C++: eine ausführliche Anleitung
What is it and why? (Real-life analogy)
Imagine you go to a fast food restaurant. You walk up to the counter and say, "Make me a burger." The cook takes a patty, a bun, some vegetables, and wraps it all in paper. Now imagine you don't say "make me a burger," but instead give the cook instructions right on the spot: "Take the patty, fry it for 3 minutes, put it on the bun, add a lettuce leaf, and wrap it up." This is a lambda function — you are not using a pre-prepared recipe (a regular function), but creating it "on the fly," right where it is needed.
In programming, situations often arise where you need to perform some simple action, but writing a separate function for it is like calling a head chef for a single sandwich. Lambda functions allow you to define behavior directly in the code, without the distraction of creating named functions.
Important: Lambda functions are not magic, but simply convenient syntactic sugar. Internally, the compiler still creates a regular function but hides this process from you.
Theory: How it works internally
To understand lambda functions, you need to look "under the hood" of the compiler. When you write a lambda, the compiler creates an anonymous class (a class without a name) with the operator(). This class is called a functor or a function object.
Consider the simplest lambda:
auto lambda = [](int x) { return x * 2; };
The compiler transforms it into something like this:
class __AnonymousLambdaClass {
public:
int operator()(int x) const {
return x * 2;
}
};
auto lambda = __AnonymousLambdaClass();
Now it becomes clear why lambdas can "capture" variables from the outer scope. If you write [=] or [&], the compiler adds corresponding fields to this anonymous class.
int multiplier = 3;
auto lambda = [multiplier](int x) { return x * multiplier; };
// The compiler creates:
class __AnonymousLambdaClass {
int multiplier; // captured variable
public:
__AnonymousLambdaClass(int m) : multiplier(m) {}
int operator()(int x) const {
return x * multiplier;
}
};
Important: By default, a lambda function isconst— it cannot modify captured variables. To allow modification, you need to add themutablekeyword.
Syntax in C++: All forms and variants
The full syntax of a lambda function looks like this:
[capture](parameters) specifiers return_type { body }
Let's break down each part:
| Part | Description | Example |
|---|---|---|
[capture] |
Defines which variables from the outer scope are accessible inside the lambda | [x, &y] |
(parameters) |
Input parameters, like a regular function | (int a, int b) |
specifiers |
Additional modifiers: mutable, constexpr, noexcept |
mutable |
return_type |
Explicit specification of the return type (optional) | -> int |
{ body } |
The code that executes when the lambda is called | { return a + b; } |
Variable capture options
| Syntax | What it does |
|---|---|
[] |
Captures nothing |
[x] |
Captures x by value (copy) |
[&x] |
Captures x by reference |
[=] |
Captures all used variables by value |
[&] |
Captures all used variables by reference |
[=, &x] |
All by value, except x (by reference) |
[&, x] |
All by reference, except x (by value) |
[this] |
Captures the current class object (for methods) |
Specifiers
- mutable — allows modifying captured-by-value variables inside the lambda
- constexpr — indicates that the lambda can be evaluated at compile time
- noexcept — guarantees that the lambda does not throw exceptions
Code examples — from simple to complex
Example 1: Simplest lambda
#include <iostream>
int main() {
// Create a lambda that takes two numbers and returns their sum
auto sum = [](int a, int b) {
return a + b;
};
// Call the lambda like a regular function
int result = sum(5, 3);
std::cout << "Sum: " << result << std::endl; // Output: Sum: 8
return 0;
}
Explanation: auto sum — the compiler determines the lambda type itself. [](int a, int b) — empty capture and two parameters. { return a + b; } — the body