How to remove a std::function<void()> in vector?

If you are willing to limit Delegate to only using function pointers the you can do it with what you have. That would look like

struct Delegate {
    std::vector<void(*)()> funcs;

    template<class T> void operator+=(T mFunc)
    {
        funcs.push_back(mFunc);
    }

    template<class T> void operator-=(T mFunc)
    {
        funcs.erase(std::remove(funcs.begin(), funcs.end(), mFunc), funcs.end());
    }

    void operator()() {
        for (auto& f : funcs) f();
    }
};

If you don't want to do so, then you need to change you approach. You could have operator += return an index to the inserted function, and then you can change operator -= to take that index and remove that element. see eerorika's answer for a suggestion on how to return iterators to the functions.


Is this possible in C++?

Not like this. Function wrappers cannot be compared for equality. This is a limitation in their design.

One option is to use function pointers. They can be compared for equality. But then you cannot use stateful function objects. NathanOliver shows an example of this.

Another alternative design would be to use a std::list as the container, and when ever you register a function, return iterator to it. Then, instead of erasing by passing the function, you can pass the iterator to be erased.


std::function objects are not directly comparable, but if you are only using regular functions (not e.g. member functions or capturing lambdas), you can use target() method to extract underlying pointer.

void operator-=(void(*mFunc)())
{
    auto pred = [&mFunc](const std::function<void()>& func) { return mFunc == *func.target<decltype(mFunc)>(); };
    funcs.erase(std::remove_if(funcs.begin(), funcs.end(), pred), funcs.end());
}

I changed the type T to be function pointer explicitly, because std::function would need a different approach (calling target() on that too). You can overload your operator -= to handle std::function separately.

It's ugly, but it works.


Note: Above snippet doesn't really take into account type safety. target() will return nullptr if type declared by template does not match the actual type stored by std::function, and dereferencing nullptr will be disastrous. Since your code only seems to deal with free functions of signature void(), it shouldn't be much issue, but if you plan to use lambdas or something it may break.

Tags:

C++