Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Returning a lambda capturing a local variable

Today I encountered a very unintuitive behavior (for me, at least) in C++11 lambdas. The code in question is the following:

#include <stdio.h>

auto sum(int x) {
    return [&x](int y) {
        return x + y;
    };
}

int main() {
    int a = sum(2)(3);
    printf("%d\n",a);
}

Instead of printing 5, this prints gibberish. Actually, at least in my version of GCC, if I turn on the -O2 optimization flag, it actually prints 5. Since the output depends on the optimization level of the compiler, it is undefined behavior. After a while, I think I understood what is happening.

When the function sum is called, a stack variable corresponding to the argument x is set to 2, then the function sum returns, and this stack variable might be overwritten by anything that the compiler needs to put there to execute following code, and by the time the lambda eventually gets executed, the place where x was no longer holds 2, and the program adds 3 to an arbitrary integer.

Is there any elegant way to do currying in C++ guaranteeing that the variable gets captured correctly?

like image 534
Felipe Lopes Avatar asked May 17 '17 01:05

Felipe Lopes


People also ask

Can lambda function access local variables?

Local variables from outer scope can be captured inside Lambda in 2 modes i.e.

Can local variables be returned?

How to return a local variable from a function? But there is a way to access the local variables of a function using pointers, by creating another pointer variable that points to the variable to be returned and returning the pointer variable itself.

How do you capture variables in lambda?

Capture clause A lambda can introduce new variables in its body (in C++14), and it can also access, or capture, variables from the surrounding scope. A lambda begins with the capture clause. It specifies which variables are captured, and whether the capture is by value or by reference.

How do you capture a variable in lambda C++?

Much like functions can change the value of arguments passed by reference, we can also capture variables by reference to allow our lambda to affect the value of the argument. To capture a variable by reference, we prepend an ampersand ( & ) to the variable name in the capture.


1 Answers

int x has a limited lifetime. References to automatic storage variables (what you call "the stack") are only valid over the variable's lifetime. In this case, only until the end of the stack frame (the scope) where the variable exists, or the function for function arguments.

[&] captures any mentioned ("local") variable by reference, except this (which is captured by value if used or implicitly used). [=] captures any mentioned variable by value. [x] would capture x explicitly, and [&x] by reference explicitly. In C++17, [*this] also works.

There is also [x=std::move(x)], or [blah=expression].

In general, if the lambda will outlive the current scope don't use [&]: be explicit about what you capture.

like image 81
Yakk - Adam Nevraumont Avatar answered Nov 14 '22 23:11

Yakk - Adam Nevraumont