Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

C++ Lambdas: Difference between "mutable" and capture-by-reference

Tags:

c++

c++11

lambda

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?

like image 420
Sebastian Mach Avatar asked Jun 05 '13 16:06

Sebastian Mach


People also ask

Does lambda capture reference by value?

The mutable keyword is used so that the body of the lambda expression can modify its copies of the external variables x and y , which the lambda expression captures by value. Because the lambda expression captures the original variables x and y by value, their values remain 1 after the lambda executes.

What is capture in lambda function?

Captures default to const value. By default, variables are captured by const value . This means when the lambda is created, the lambda captures a constant copy of the outer scope variable, which means that the lambda is not allowed to modify them.

What is a lambda capture list?

The capture list defines the outside variables that are accessible from within the lambda function body. The only capture defaults are. & (implicitly capture the used automatic variables by reference) and. = (implicitly capture the used automatic variables by copy).

Which of the following operator is used to capture all the external variable by reference?

5. Which of the following operator is used to capture all the external variable by reference? Explanation: The lambda expression uses & operator to capture the external variable by reference.


1 Answers

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.

like image 147
Sebastian Mach Avatar answered Nov 01 '22 19:11

Sebastian Mach