1

I need to pass a std::function as a void* pointer to be executed asynchronously, so I create a std::function on the heap. Is it safe to delete the std::function object from inside the function body?

Please see the following example:

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

void fun(int data)
{
    T* t = new T([&t, data]()
    {
        //do something
        delete t;
    }

    ExecuteAsync(t);
}
7
  • 4
    Safe as long as you don't do anything in the lambda after delete. This is similar to Is "delete this" allowed in C++?. Commented Jan 9, 2024 at 9:38
  • 2
    Isn't &t going to capture t by reference though, i.e. ending up with a dangling reference after fun returns? Commented Jan 9, 2024 at 9:50
  • 1
    This is poor design. You are creating a tight coupling between the subroutine and how it’s memory allocation is handled. Commented Jan 9, 2024 at 9:58
  • 1
    Why is ExecuteAsync taking a std::function<void()>*? Commented Jan 9, 2024 at 10:09
  • 1
    to pass a std::function as a void pointer Are you absolutely sure the ExecuteAsync function expects a pointer to exactly std::function<void()>? That is very odd. Who wrote the function? Are you sure the function doesn't take a void (*)() pointer? What is the exact full actual declaration of ExecuteAsync function? Commented Jan 9, 2024 at 10:18

1 Answer 1

1

Mistake in capturing &t

Firstly, note that capturing [&t] is a mistake. &t would dangle when fun returns. Either you'd need to allocate the std::function first, or you'd need to use C++23 explicit object member functions to make this work:

// C++17
auto* t = new std::function<void()>;
*t = [t, data] { /* do something */; delete t; };

// C++23
auto* t = new std::function<void()>([data](this auto&& self) {
    // do something
    delete &self;
};

Assuming you did one of these two things ...

Delete self in std::function

Pedantically speaking, it is not safe to do. [func.wrap.func] operator() is only said to return INVOKE<R>(...), but it's not guaranteed to consist of just a return statement that does that. Hypothetically, it would be allowed to implement it like(1):

R operator()(ArgTypes... args) const {
    R result = INVOKE<R>(f, std​::​forward<ArgTypes>(args)...);
    // read members of this object
    return result;
}

Reading the memory of an object whose lifetime has ended is obviously UB, so this would break your code. However, no standard library does this, and there is no obvious reason why anyone would.

In practice, what you're doing will work, for the same reason that delete this is allowed. See Is "delete this" allowed in C++?.


(1) A Returns paragraph only states the returned value. It does not impose further requirements on the implementation.

Further notes on design

However, if you have to ask yourself, maybe it's not a good idea from a code style perspective. If you instead did:

ExecuteAsync(std::move(my_function));

... where ExecuteAsync obtains ownership of a std::function and also destroys it once executed, it would be much more obvious how and why it's correct. At the very least, you could hand ExecuteAsync a std::unique_ptr.

Sign up to request clarification or add additional context in comments.

4 Comments

Normally I would use move semantics as you have mentioned but that is a quite specific case. I work with a legacy code and ExecuteAsync is defined this way: void ExecuteAsync(void* fun)
@Irbis so ExecuteAsync then casts void* fun to std::function<void()>* or what's going on? This is pretty bizarre.
Yes, maybe I have made a mistake that I have generalized the example, but the real use case is to pass std::function<void()>* using winapi PostMessage LPARAM. When the message is processed the std::function<void()>*is cast this way: T* t = (T*)(msg.lParam)
@Irbis you can probably work with function pointer instead, and use int data as the LPARAM data instead of std::function. That's closer to how the WinAPI is expected to be used, I imagine.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.