Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

c++ lambda capture by value

Tags:

c++

c++11

I am reading through the C++ lambda section in chapter 3 of this book and the following code confuses me:

int x = 0;
int y = 42;
auto qqq = [x, &y] {
    std::cout << "x: " << x << std::endl;
    std::cout << "y: " << y << std::endl;
    ++y;
};
x = y = 77;
qqq();
qqq();
std::cout << "final y: " << y << std::endl;

This code prints out:

x: 0
y: 77
x: 0
y: 78
final y: 79

Why does qqq() not register that x has changed to 77? It was stated that passing by value means we can read but not modify data readable where the lambda was defined. Does that mean we can't see changes after it's definition?

like image 821
Lawrence Avatar asked Jun 05 '16 00:06

Lawrence


3 Answers

That's because the variable is captured by value (i.e. copied) only once, when you define the lambda. It's not "updated" as you may believe. The code is roughly equivalent to:

#include <iostream>

int x = 0;
struct Lambda
{
    int _internal_x; // this is used to "capture" x ONLY ONCE
    Lambda(): _internal_x(x) {} // we "capture" it at construction, no updates after
    void operator()() const
    {
        std::cout << _internal_x << std::endl;
    }
} qqq; 

int main()
{
    qqq(); 
    x = 77; // this has no effect on the internal state of the lambda
    qqq();
}

Live on Coliru

like image 171
vsoftco Avatar answered Nov 20 '22 00:11

vsoftco


By binding a variable by value to a lambda closure, you're effectively copying the value of the variable into a separate variable that is found inside the lambda object. Likewise, by binding a variable by reference, you're making such an internal variable to be a reference to the original variable instead, thus being able to "see changes" on the original variable.

Look at it like this. The following code...

#include <iostream>

int main()
{
    int x = 0;
    int y = 42;
    auto qqq = [x, &y] {
        std::cout << "x: " << x << std::endl;
        std::cout << "y: " << y << std::endl;
        ++y;
    };
    x = y = 77;
    qqq();
    qqq();
    std::cout << "final y: " << y << std::endl;
}

Is (kind of, but not exactly) syntactic sugar for...

#include <iostream>

class MyLambda
{
    private:
        int x;
        int& y;

    public:
        MyLambda(int x, int& y) : x(x), y(y) {}

        void operator()()
        {
            std::cout << "x: " << x << std::endl;
            std::cout << "y: " << y << std::endl;
            ++y;
        }
};

int main()
{
    int x = 0;
    int y = 42;
    MyLambda qqq = MyLambda(x, y);
    x = y = 77;
    qqq();
    qqq();
    std::cout << "final y: " << y << std::endl;
}

With the exception of the special lambda syntax and the fact that you can't reference the lambda's type directly. By comparing the former with the later code, you should be able to clear out your understanding of closures.

like image 37
3442 Avatar answered Nov 20 '22 00:11

3442


The lambda uses the value at the time the lambda is defined (it makes a copy), not at the time it is invoked.

This might help: How are Lambda Closures Implemented?

like image 7
jdigital Avatar answered Nov 19 '22 23:11

jdigital