Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Deleting a std::function in the middle of invocation

Tags:

c++

c++11

c++14

Is it undefined behavior to be destroying/deleting a std::function while in the middle of invocation?

class Event {
  public:
    Event(std::function<void()> f) : func(std::move(f)) {}
    ~Event() {}
    std::function<void()> func;
};

int main()
{
    std::vector<Event> events;
    auto func = [&]() {
      events.pop_back();  
      std::cout << "event" << std::endl;
      // do more work  
    };

    events.emplace_back(std::move(func));
    events[0].func();

    return 0;
}
like image 444
Mozbi Avatar asked Aug 09 '18 20:08

Mozbi


People also ask

Can std :: function be empty?

The stored callable object is called the target of std::function . If a std::function contains no target, it is called empty.

Why destructor is used when delete is there?

When delete is used to deallocate memory for a C++ class object, the object's destructor is called before the object's memory is deallocated (if the object has a destructor). If the operand to the delete operator is a modifiable l-value, its value is undefined after the object is deleted.

In what way destructor is different from delete operator?

So delete is for managing the dynamic memory, but the destructor is a method of the class itself, which is always called when the object is getting freed from the memory (stack or heap).


2 Answers

This is undefined by [res.on.objects]p2:

If an object of a standard library type is accessed, and the beginning of the object's lifetime does not happen before the access, or the access does not happen before the end of the object's lifetime, the behavior is undefined unless otherwise specified.

The "access" in this case consists of the call to the function call operator of the std::function. The std::function object's lifetime ended at the pop_back() call, in the middle of the access. Therefore, the access does not happen before the end of the object's lifetime, and the behavior is undefined.

like image 83
T.C. Avatar answered Oct 07 '22 13:10

T.C.


Your code (the bit you're asking about, i.e. the destructing of the object inside a member function) is roughly equivalent to

struct A
{
  void f() { delete this; }
}

int main()
{
  A* a = new A;
  a.f();
}

This does actually work. Refcounted resources could do something similar when the refcount reaches zero in their unref function.

Note you might want to rethink tying the event list and events themselves together like this. An event shouldn't know about its environment (the event queue).

like image 31
rubenvb Avatar answered Oct 07 '22 12:10

rubenvb