Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Lambda capture by value and the "mutable" keyword

Tags:

c++

lambda

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

like image 579
Arnbjorg Johansen Avatar asked Feb 05 '17 19:02

Arnbjorg Johansen


2 Answers

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.

like image 67
skypjack Avatar answered Nov 06 '22 17:11

skypjack


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;
};
like image 32
Alex Guteniev Avatar answered Nov 06 '22 17:11

Alex Guteniev