73

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

97

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.

7
  • 1
    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.
    – aschepler
    Commented Jun 5, 2013 at 18:52
  • @aschepler: Thanks for the suggestions :) I changed to reserved names. Commented Jun 6, 2013 at 9:33
  • 3
    @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. Commented Jul 30, 2013 at 8:27
  • 1
    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.
    – legends2k
    Commented Jun 12, 2014 at 13:15
  • 3
    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
    Commented Dec 10, 2014 at 1:10

Not the answer you're looking for? Browse other questions tagged or ask your own question.