Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Lambda Capture and Memory Management

When I capture an object by reference in a C++11 lambda, let the object go out of scope, and then execute the lambda, it still has access to the object. When I execute the following code, the lambda call can still access the object, although the destructor has already been called! Can someone explain why this works and why I don't get a runtime error?

#include <iostream>

class MyClass {
public:
    int health = 5;
    MyClass() {std::cout << "MyClass created!\n";}
    ~MyClass() {std::cout << "MyClass destroyed!\n";}
};

int main(int argc, const char * argv[])
{
    std::function<bool (int)> checkHealth;
    if(true) {
        MyClass myVanishingObject;
        checkHealth = [&myVanishingObject] (int minimumHealth) -> bool {
            std::cout << myVanishingObject.health << std::endl;
            return myVanishingObject.health >= minimumHealth;
        };
    } // myVanishingObject goes out of scope

    // let's do something with the callback to test if myVanishingObject still exists.
    if(checkHealth(4)) {
        std::cout << "has enough health\n";
    } else {
        std::cout << "doesn't have enough health\n";
    }
    return 0;
}

Here's the output:

MyClass created!
MyClass destroyed!
5
has enough health
like image 275
basteln Avatar asked Feb 08 '13 00:02

basteln


1 Answers

According to the cppreference.com website's documentation of lambda functions

Dangling references

If an entity is captured by reference, implicitly or explicitly, and the function call operator of the closure object is invoked after the entity's lifetime has ended, undefined behavior occurs. The C++ closures do not extend the lifetimes of the captured references.

In other words, the fact that you have captured the object by reference and then let the object's lifetime ends means that invoking the lambda causes undefined behavior. Since one possible way that UB might work is "the object appears to be alive and well even though the object is dead," I suspect that you are seeing undefined behavior manifesting itself as nothing appearing to have gone wrong.

I suspect this would be the case if the compiler allocated a unique stack location to the temporary variable. This would mean that after the lifetime of the object ends, before main returns, the memory wouldn't be touched by anything. Accordingly, you'd see the variable holding the value 5 just as before, since nothing else is writing on top of it.

Hope this helps!

like image 86
templatetypedef Avatar answered Sep 19 '22 23:09

templatetypedef