5
votes

The need of the keyword mutable in lambdas, is source of great confusion.

Consider the code:

int x = 10;

function<void()> lambda = [=]() mutable {x++; cout << "Inside lambda: x = " << x << "\n";};

cout << "Before lambda: x = " << x << "\n";
lambda();
cout << "After  lambda: x = " << x << "\n\n";

Output:

Before lambda: x = 10
Inside lambda: x = 11
After  lambda: x = 10

As we can see, the variable x stays unchanged after the lambda, so there are no side effects.

However, if we "forget" the keyword mutable, we get an error.

Being the argument passing by value the default in C++, it doesn't make sense for me the need of the mutable keyword.

Can someone write (even in pseudo code) the class generated by the compiler, in place of the lambda?

Thank you

3

3 Answers

5
votes

As mentioned here the mutable specifier allows the lambda to modify the parameters captured by copy and to call their non-const member functions. It doesn't affect variables captured by reference.

Can someone write (even in pseudo code) the class generated by the compiler, in place of the lambda?

It's not that easy to give a general case, but we can define something that is valid in your specific case.
The generated class would probably look like:

struct Lambda {
    void operator()() { x++; }
    int x{10};
};

If you remove the mutable specifier, the function operator is defined as const:

struct Lambda {
    void operator()() const { x++; }
    int x{10};
};

For simplicity I've initialized x with the given value (10) and made it public, but it's obviously initialized by copy using the variable captured from the surrounding context and not accessible from outside the function operator.
Its type is deduced as well from the variable used to initialize it as if you do:

auto lambda_x = x;

See here for further details.

1
votes
class Lambda
{
public:
   Lambda(const Lambda&);
   ~Lambda();

   // the main functor operator, const when lambda not mutable
   R operator()(Args args) const;

   // Only present for non-capture lambda         
   operator PlainCFunctionType () const; 

   // Only present for non-capture  lambda         
   PlainCFunctionType operator+() const;
private:
   // Gets called when lambda created, but you can't call it yourself
   Lambda(Captures captures...); 

   Captures captures;
};
1
votes

You're not really writing code in an object-oriented way, but anyhow. Normally the external variables added by [=] are const values. By adding the mutable keyword, you create local modifiable copies of them. You could use [&] instead to capture by reference.