FrameBuffer
FrameBuffer

Reputation: 836

What exactly happens if I delete inside a lambda the object that holds that lambda?

I have a callback system that holds lambdas to be emitted when something happens. To get notified you have to register the lambda to an identifier, or unregister if you don't want to be notified again.

The problem that I have is that I have lambdas registered that on called will unregister from that system causing the destruction of the current lambda being executed. And I think this is not safe. But I'm not sure.

Simplifying for example:

#include <map>
#include <functional>
#include <iostream>


int main() {
  std::map<int, std::function<void ()>> m;
  m[10] = [&m] () {
    int i = m.size();  // Just cheking the internal state of the lambda
    m.clear();
    //i += m.size();   // If I uncomment this the std::cout is not working.
    std::cout<< "What happens? " << i << std::endl;
  };

  m[10]();

  return 0;
}

What I'm seeing is that when I check the state of the lambda after m.clear(), I get strange behaviour (for example std::cout not working). Can you explain me what happens exactly in those cases?

How will you handle those situations with callbacks? making a copy before calling the callback (seems a no-no)?

Upvotes: 4

Views: 1232

Answers (1)

Vittorio Romeo
Vittorio Romeo

Reputation: 93324

Think about what's happening: you're storing a function object generated with a lambda expression inside an std::function<void()>.

The std::function now owns the generated function object. It also resides inside an std::map.

You then capture that same std::map by reference and invoke std::map::clear on it, removing the stored std::function.

From the std::map::clear documentation on cppreference.org:

Removes all elements from the container. Invalidates any references, pointers, or iterators referring to contained elements. Any past-the-end iterator remains valid.

It is safe to assume that clearing an std::map invalidates the data stored inside it. Therefore using data stored inside the generated function object is unsafe and could lead to undefined behavior.


This is a rough simplification of what you're doing:

using func = std::function<void()>;

func f;
f = [&f]{ int a = 0; f.~func(); std::cout << a; };

Upvotes: 5

Related Questions