Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Figuring out why capturing by reference in a nested lambda produces a weird result

Tags:

c++

lambda

When the outer variable x is captured by value

return [=](int y){ return x * y; };

foo(2)(3) produces 6.

However if I capture x by reference

return [&](int y){ return x * y; };

foo(2)(3) produces 9.

Minimal Code

#include <iostream>
#include <functional>

int main()
{
    using namespace std;
    function<function<int(int)>(int)> foo = [](int x)
    {
        return [&](int y) { return x * y; };
    };
    cout << foo(2)(3);
    return 0;
}

Question

I cannot figure out why this happens, can you?

like image 672
xport Avatar asked Dec 09 '22 23:12

xport


2 Answers

x is local to the outer function, so it's destroyed as soon as that function returns.

Consider a simpler example.

#include <iostream>
#include <functional>

int& foo(int x) {
    return x;
}

int main()
{
    using namespace std;
    int& b = foo(5);
    return 0;
}

Here it's easier to see that b is a dangling reference.

Next we go ahead and instead of returning the reference directly, we return a lambda that captures the variable by reference.

#include <iostream>
#include <functional>

auto foo(int x) {
    return [&x]() {return x;};
}

int main()
{
    using namespace std;
    auto b = foo(5);
    return 0;
}

The problem still remains. Once the lambda is returned, x has already gone out of scope and is no longer alive. But the lambda holds a (dangling) reference to it.

like image 200
super Avatar answered Dec 12 '22 11:12

super


When the lambda capture x by-reference, when the invocation of foo(2) ends, the parameter x gets destroyed, then the reference captured to it becomes dangling. The dereference on it in the invocation of foo(2)(3) leads to UB, anything is possible.

Capture-by-value doesn't have such invalid reference issue.

like image 34
songyuanyao Avatar answered Dec 12 '22 12:12

songyuanyao