C ++ 11 Closing a lambda that includes a stack variable by reference that leaves the scope visible, but getting undefined behavior?

I know C ++ well. I used lambdas and closures in other languages. For my training, I wanted to see what I can do with them in C ++.

Fully aware of the "danger" and expecting the compiler to reject it, I created a lambda in the function using the function stack variable by reference and returned the lambda. The compiler allowed this, and strange things happened.

Why did the compiler allow this? Is it just a compiler’s question that couldn’t detect that I did something very, very bad, and the results are just “w90 behavior”? Is this a compiler problem? Does the specification have anything to say about this?

Tested on the latest mac, with MacPorts gcc 4.7.1 installed and the -std = C ++ 11 option.

Used code:

#include <functional> #include <iostream> using namespace std; // This is the same as actsWicked() except for the commented out line function<int (int)> actsStatic() { int y = 0; // cout << "y = " << y << " at creation" << endl; auto f = [&y](int toAdd) { y += toAdd; return y; }; return f; } function<int (int)> actsWicked() { int y = 0; cout << "actsWicked: y = " << y << " at creation" << endl; auto f = [&y](int toAdd) { y += toAdd; return y; }; return f; } void test(const function<int (int)>& f, const int arg, const int expected) { const int result = f(arg); cout << "arg: " << arg << " expected: " << expected << " " << (expected == result ? "=" : "!") << "= " << "result: " << result << endl; } int main(int argc, char **argv) { auto s = actsStatic(); test(s, 1, 1); test(s, 1, 2); test(actsStatic(), 1, 1); test(s, 1, 3); auto w = actsWicked(); test(w, 1, 1); test(w, 1, 2); test(actsWicked(), 1, 1); test(w, 1, 3); return 0; } 

Results:

 arg: 1 expected: 1 == result: 1 arg: 1 expected: 2 == result: 2 arg: 1 expected: 1 != result: 3 arg: 1 expected: 3 != result: 4 actsWicked: y = 0 at creation arg: 1 expected: 1 == result: 1 arg: 1 expected: 2 == result: 2 actsWicked: y = 0 at creation arg: 1 expected: 1 == result: 1 arg: 1 expected: 3 != result: 153207395 
+4
source share
1 answer

The return of the lambda, which captures the local variable by reference, matches the return of the link to the local variable directly; this leads to undefined behavior:

5.1.2 Lambda Expressions [expr.prim.lambda]

22 - [Note. If the object is implicitly or explicitly captured by the link, calling the operator of the function call of the corresponding lambda expression after the end of the service life of the object can lead to undefined behavior. -end note]

In particular, the undefined behavior in this case is in the lvalue-to-rvalue transformation:

4.1 Lvalue-to-rval conversion [conv.lval]

1 - The value gl (3.10) of a non-functional type without an array T can be converted to prvalue. If T is an incomplete type, a program that requires this conversion is poorly formed. If the object to which the glvalue belongs is not an object of type T and is not an object of a type derived from T, or if the object is not initialized, the program that requires this conversion has undefined behavior.

The compiler is not required to diagnose this form of undefined behavior, although as compiler support for lambdas improves, it is likely that compilers will be able to diagnose this case and offer an appropriate warning.

Since lambda closure types are well defined, just opaque, your example is equivalent:

 struct lambda { int &y; lambda(int &y): y(y) {}; int operator()(int toAdd) { y += toAdd; return y; }; } f{y}; return f; 

In general terms, C ++ solves the funarg problem, giving it the responsibility of the programmer and providing opportunities (mutable lambda capture, move semantics, unique_ptr , etc.) to allow the programmer to solve it effectively.

+10
source

Source: https://habr.com/ru/post/1434653/


All Articles