DevAcademia lesson/C++ Functions
C++ Lambda Functions
Topic progressTopic 82 of 109 - 75% complete
~9 min lesson30 learning blocks5 code examples1 checks/interactions
Lesson outline
Introduction to Lambdas
Lambda functions (or lambda expressions) are anonymous, inline function objects defined where they are used.
Introduced in C++11, they provide a concise way to define function objects without creating a separate named function or functor.
Basic Lambda Syntax
Components:
- Capture: Which surrounding variables are available inside
- Parameters: Like regular function parameters (optional)
- Return type: Often omitted; deduced automatically (in C++11 it must be explicit unless the body is a single return statement)
- Body: The actual logic
Simple Lambda Example
Capture Clauses
| Capture Type | Syntax | Description |
|---|---|---|
| By value (default all used) | [=] | Copies each used variable at lambda creation time; NOTE: this is implicitly captured (as a pointer), not copied |
| By reference (default all used) | [&] | Binds each used variable by reference; variables must outlive the lambda |
| Specific by value / reference | [x, &y] | Captures x by value, y by reference |
| Mixed defaults | [=, &x] or [&, x] | Default plus exceptions; the default must appear first |
| None | [] | Captures nothing; only parameters and globals may be used |
| Init-capture (C++14+) | [p = std::move(ptr)] | Creates a new captured variable from an expression (great for move-only types) |
| Capture this | [this] / [*this] (C++17+) | [this] captures the this pointer; [*this] captures a copy of the current object |
Mutable Lambdas
Return Type Deduction
C++14 introduced generic lambdas (auto parameters) and init-capture.
- Return type is usually deduced automatically.
- In C++11, omit the return type only if the body is a single return statement; otherwise use a trailing return type (e.g.,
-> double). - C++20 allows a template parameter list on lambdas for more control.
Real-World Use Cases
- Standard Algorithms: Predicates and comparators
- Event Handlers / Callbacks: Small inline actions
- Concurrency: Tasks for threads or async operations
- Deferred Execution: Store work for later (e.g., in containers)
- Scope Guards: Automatic cleanup logic
Conversions & Object Model
Each lambda has a unique, unnamed closure type. Non-capturing lambdas are convertible to function pointers; capturing lambdas are not (but can be stored in std::function with type-erasure).
Best Practices
- Keep lambdas concise and focused on a single task
- Prefer explicit, minimal capture lists over broad [=] or [&]
- Use 'mutable' only when necessary; understand it affects only by-value captures
- For complex logic or reuse, prefer named functions/functors
- Always consider variable lifetimes, especially with reference captures and concurrency
Was this topic clear?