对函数使用擦除-删除习语<void()>

Using erase-remove idiom for function<void()>

人山人海。

我正在尝试为我的程序实现一个观察者(esque?)模式。我有一个组件,用于存储发生事件时应调用的函数。我的问题是,如果需要的话,我不知道应该如何从容器中删除我的功能。尝试通过引用存储函数,但我不确定该怎么做(或者如果可能的话。)

#include <map>
#include <vector>
#include <iostream>
#include <algorithm>
#include <functional>
using namespace std;

enum class EVENT_TYPE{ 
    anEvent 
};

class EventableComponent{
    map<EVENT_TYPE, vector<function<void()>>> listeners;
public:
    void trigger(EVENT_TYPE _et){
        for(auto& it : listeners[_et]){
            it();
        }
    }

    void registerListener(EVENT_TYPE _et, function<void()> _fn){
        listeners[_et].push_back(_fn);
    };

    void removeListener(EVENT_TYPE _et, function<void()> _fn){
        //error C2678: binary '==' : no operator found which takes a left-hand operand of type 'std::function<void (void)>' 
        //(or there is no acceptable conversion)        
        listeners[_et].erase(remove(listeners[_et].begin(), listeners[_et].end(), _fn), listeners[_et].end());
    };
};

int main(){
    EventableComponent ec;

    // this would become a member function for a class  somewhere down the line
    auto fn = [](){cout << "Hello.\n"; };

    ec.registerListener(EVENT_TYPE::anEvent, fn);
    ec.trigger(EVENT_TYPE::anEvent);

    ec.removeListener(EVENT_TYPE::anEvent, fn);
    ec.trigger(EVENT_TYPE::anEvent);

    cin.get();
    return 0;
};

您的问题可以简化为无法比较两个 std::function 实例的相等性。 std::remove需要operator==std::function没有。参见 "Why is std::function not equality comparable?"

考虑以下情况。

假设您在 main:

中定义了两个 lambda
auto fn = [](){cout << "Hello.\n"; };
auto fn2 = [](){cout << "Hello.\n"; };

现在,这两个是否相等?他们做同样的事情,但这也许纯属巧合。如果第二个 "Hello" 变成 "Hello2",它们会变得不相等吗?如果第二个不再是 lambda 而是一个实函数,它们会变得不相等吗 void f()?

问题是函数对象没有通常有用的相等性定义,所以由您来定义相等性在[=46的上下文中的真正含义=]你的程序。

您有多种选择来解决手头的问题。一种是对 指针 std::function 对象进行操作。可以比较指针,正确使用 std::unique_ptr 可确保正确处理释放。

或者您为每个使用的 std::function 分配一个标识符。请参阅以下修改后的代码示例,其中向量中 std::function<void()> 的直接存储被替换为将 int 映射到函数对象的自定义类型 EventFunction。该示例使用 std::remove_if 仅比较 ints:

#include <map>
#include <vector>
#include <iostream>
#include <algorithm>
#include <functional>
using namespace std;

enum class EVENT_TYPE{ 
    anEvent 
};

struct EventFunction {
    function<void()> f;
    int id;
};

class EventableComponent{
    map<EVENT_TYPE, vector<EventFunction>> listeners;
public:
    void trigger(EVENT_TYPE _et){
        for(auto& it : listeners[_et]){
            it.f();
        }
    }

    void registerListener(EVENT_TYPE _et, EventFunction _fn){
        listeners[_et].push_back(_fn);
    };

    void removeListener(EVENT_TYPE _et, int function_id){
        //error C2678: binary '==' : no operator found which takes a left-hand operand of type 'std::function<void (void)>' 
        //(or there is no acceptable conversion)     
        listeners[_et].erase(remove_if(listeners[_et].begin(), listeners[_et].end(),
               [&](EventFunction const& e) { return e.id == function_id; }), listeners[_et].end());
    };
};

int main(){
    EventableComponent ec;

    // this would become a member function for a class  somewhere down the line
    auto fn = [](){cout << "Hello.\n"; };

    ec.registerListener(EVENT_TYPE::anEvent, EventFunction{ fn, 1 });
    ec.trigger(EVENT_TYPE::anEvent);

    ec.removeListener(EVENT_TYPE::anEvent, 1);
    ec.trigger(EVENT_TYPE::anEvent);
}; 

Tried storing the functions by reference, but iam not sure how to do that(or if thats possible.)

这是不可能的,因为您不能在标准库容器中存储引用。但我想这个想法与我上面提到的带有指针的想法类似。