Lambda Capture by Value and Reference in C++
Lambda expressions in modern C++ allow anonymous functions to capture
variables from their enclosing scope, determining how data outside the
lambda is accessed and managed. This article explains how variable
capture works in C++, focusing on capture by value versus capture by
reference, the syntax for explicit and default captures, the behavior of
the mutable keyword, and best practices for avoiding common
pitfalls such as dangling references.
The Capture Clause
A lambda expression introduces variable captures through its capture
clause, denoted by brackets [] at the beginning of the
expression. Variables within the surrounding lexical scope can be
captured explicitly by name or implicitly via capture defaults.
[capture_list](parameters) -> return_type {
// function body
};Capture by Value
When a variable is captured by value, the lambda creates an independent copy of that variable at the moment the lambda is defined.
Syntax
To capture a variable x by value, include its name
inside the brackets without an ampersand:
int x = 10;
auto lambda = [x]() {
return x * 2;
};Immutability and the
mutable Keyword
By default, the function call operator (operator()) of a
lambda is marked const. This means captured-by-value
variables cannot be modified inside the lambda body.
To modify the local copy of a captured-by-value variable, declare the
lambda as mutable:
int count = 0;
auto increment = [count]() mutable {
count++; // Modifies the local copy, not the external 'count'
return count;
};
increment(); // returns 1
increment(); // returns 2
// The external 'count' variable remains 0.Advantages and Risks
- Advantage: Safe for asynchronous or delayed execution (e.g., thread pools or callbacks) because the lambda owns its own copy of the data.
- Risk: Copying large objects can cause performance overhead.
Capture by Reference
When a variable is captured by reference, the lambda stores a reference to the original variable rather than making a copy. Any reads or writes within the lambda directly interact with the original object.
Syntax
To capture a variable by reference, prefix the variable name with an
ampersand &:
int total = 0;
auto addToTotal = [&total](int amount) {
total += amount; // Directly modifies the external 'total'
};
addToTotal(5); // 'total' is now 5Because the lambda operates on references, modifying the variable
does not require the mutable specifier unless the
referenced variable itself is const.
Advantages and Risks
- Advantage: Zero copying overhead, making it ideal for passing heavy objects or accumulating state.
- Risk (Dangling References): If a lambda outlives the scope of the captured variable (such as returning a lambda from a function or passing it to an asynchronous worker), accessing the captured reference causes undefined behavior.
Default Captures
C++ provides shorthand syntax to capture all referenced external variables automatically:
[=](Default Capture by Value): Implicitly captures any variable used within the lambda body by value.[&](Default Capture by Reference): Implicitly captures any variable used within the lambda body by reference.
Mixed Captures
You can combine a default capture mode with explicit exceptions:
int a = 1, b = 2, c = 3;
// Capture everything by value, but capture 'c' by reference
auto lambda1 = [=, &c]() {
c = a + b;
};
// Capture everything by reference, but capture 'a' by value
auto lambda2 = [&, a]() {
b += a;
};Note: You cannot use the same capture mode explicitly that is
already established by the default (e.g., [=, a] is
invalid).
Summary of Differences
| Feature | Capture by Value ([x]) |
Capture by Reference
([&x]) |
|---|---|---|
| Data Mechanism | Stores an internal copy | Stores a reference to the original |
| Original Variable | Remains unchanged | Modified by changes inside the lambda |
| Default Mutability | Read-only (requires mutable
to edit) |
Read/Write (if original is non-const) |
| Lifetime Safety | Safe across scope boundaries | Vulnerable to dangling references |
| Performance | Incurs copy cost for large types | Zero copy cost |