Are C ++ lambdas true closures? Link Capture

In the code below, I create a lambda that captures a local variable by reference. Note that this is a pointer, so if C ++ lambdas are true closures, it must survive the lifetime of the function that the lambda creates.

However, when I call it again, instead of creating a new local variable (new environment), it repeats the same as before, and actually captures exactly the same pointer as before.

This seems wrong. Either C ++ lambdas are not true closures, or is my code incorrect?

Thanks for any help

#include <iostream> #include <functional> #include <memory> std::function<int()> create_counter() { std::shared_ptr<int> counter = std::make_shared<int>(0); auto f = [&] () -> int { return ++(*counter); }; return f; } int main() { auto counter1 = create_counter(); auto counter2 = create_counter(); std::cout << counter1() << std::endl; std::cout << counter1() << std::endl; std::cout << counter2() << std::endl; std::cout << counter2() << std::endl; std::cout << counter1() << std::endl; return 0; } 

This code returns:

 1 2 3 4 5 

But I expected him to return:

 1 2 1 2 3 

Further editing :

Thanks for pointing out the error in my source code. Now I see that what happens is that the pointer is deleted after calling create_couter, and the new create simply reuses the same memory address.

Which brings me to my true question, then I want to do the following:

 std::function<int()> create_counter() { int counter = 0; auto f = [&] () -> int { return ++counter; }; return f; } 

If C ++ lambdas were true closures, each local counter will coexist with the returned function (the function carries its own environment - at least part of it). Instead, the counter is destroyed after a call to create_counter, and a call to the returned function generates a segmentation error. This is not the expected closure behavior.

Marco A offered a job: make the pointer a passed copy. This increases the control counter, so it does not break after create_counter. But it is kludge. But, as Marco noted, he works and does exactly what I expected.

Jarod42 suggests declaring a variable and initializing it as part of a capture list. But this defeats the goal of closure, since the variables are then local to the function, not the environment in which the function is created.

apple apple suggests using a static counter. But this is a workaround to avoid deleting the variable at the end of the create_function, which means that all the returned functions have the same variable, and not the environment in which they are executed.

So, I think the conclusion (if someone cannot shed more light) is that lambdas in C ++ are not true closures.

Thanks again for your comments.

+5
source share
4 answers

The shared pointer is destroyed at the end of the scope, and memory is freed: you keep a freezing link

 std::function<int()> create_counter() { std::shared_ptr<int> counter = std::make_shared<int>(0); auto f = [&]() -> int { return ++(*counter); }; return f; } // counter gets destroyed 

Therefore, undefined behavior is called. Test it yourself by replacing the integer with a class or structure and see if the destructor is actually called.

Capturing by value would increase the shared pointer usage counter and prevent the problem

 auto f = [=]() -> int { return ++(*counter); }; ^ 
+10
source

As already mentioned, you have a saggy reference since the local variable is destroyed at the end of the scope.

You can simplify your function to

 std::function<int()> create_counter() { int counter = 0; return [=] () mutable -> int { return ++counter; }; } 

or even (in C ++ 14)

 auto create_counter() { return [counter = 0] () mutable -> int { return ++counter; }; } 

Demo

+3
source

if you want 1 2 3 4 5 you can also try this

 std::function<int()> create_counter() { static int counter = 0; auto f = [&] () -> int { return ++counter; }; return f; } 
0
source

If the variable is captured by vaule, then it is copied from the original variable. If by reference you can consider them as different links to the same object.

0
source

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


All Articles