74

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?

1 Answer 1

98

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.

Sign up to request clarification or add additional context in comments.

7 Comments

Nice and thorough. But I think you have a some places you meant b. And your objects of anonymous class type don't have initializers. Might want to explain we're pretending lambda_a() is a constructor although the class has no name.
@AnoopK.Prabhu: In the last example, the const actually makes no difference. You always mutate the referencee through a reference. But to give another example: int main () { int x; auto a = [=]() { ++x; }; }. g++ will give an error message for that because the [=] means that the generated function object gets its own member variable x, but the lack of mutable means that a const member function call operator is generated, therefore the x is not assignable.
Excellent! +1 particularly for the above comment and the constness of a pointer/reference does not change the constness of the pointee/referencee seen through it. Adding this case auto c = [=]() { ++x; }; above would give more clarity on why mutable, and there by const, makes a difference.
The real question is why the convention is taken in reverse compared to everything else in C++. mutable qualification should be by default, and const specification should be explicit.
@v.oddou Maybe because it is too easy to accidentally modify the capture by copy, e.g., int sum = 0; std::for_each(vec.cbegin(), vec.cend(), [sum](int x) { sum += x;}).
|

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.