C++ Lambdas: Difference between "mutable" and capture-by-reference
In C++ you can declare lambdas for example like this:
int x = 5;
auto a = [=]() mutable { ++x; std::cout << x << '\n'; };
auto b = [&]() { ++x; std::cout << x << '\n'; };
Both let me modify x
, so what is the difference?
What is happening
The first will only modify its own copy of x
and leave the outside x
unchanged.
The second will modify the outside x
.
Add a print statement after trying each:
a();
std::cout << x << "----\n";
b();
std::cout << x << '\n';
This is expected to print:
6
5
----
6
6
Why
It may help to consider that lambda
[...] expressions provide a concise way to create simple function objects
(see [expr.prim.lambda] of the Standard)
They have
[...] a public inline function call operator [...]
which is declared as a const
member function, but only
[...] if and only if the lambda expression’s parameter-declaration-clause is not followed by
mutable
You can think of as if
int x = 5;
auto a = [=]() mutable { ++x; std::cout << x << '\n'; };
==>
int x = 5;
class __lambda_a {
int x;
public:
__lambda_a () : x($lookup-one-outer$::x) {}
inline void operator() { ++x; std::cout << x << '\n'; }
} a;
and
auto b = [&]() { ++x; std::cout << x << '\n'; };
==>
int x = 5;
class __lambda_b {
int &x;
public:
__lambda_b() : x($lookup-one-outer$::x) {}
inline void operator() const { ++x; std::cout << x << '\n'; }
// ^^^^^
} b;
Q: But if it is a const
function, why can I still change x
?
A: You are only changing the outside x
. The lambda's own x
is a reference, and the operation ++x
does not modify the reference, but the refered value.
This works because in C++, the constness of a pointer/reference does not change the constness of the pointee/referencee seen through it.