对函数<void()>使用erase-remove惯用法

时间:2015-10-03 12:25:21

标签: c++ functional-programming erase-remove-idiom

堆积的人。

我试图为我的程序实现一个观察者(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;
};

1 个答案:

答案 0 :(得分:3)

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

考虑以下情况。

假设您在auto fn = [](){cout << "Hello.\n"; }; auto fn2 = [](){cout << "Hello.\n"; }; 中定义了两个lambdas:

"Hello"

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

事实是,对于函数对象,没有通常有用的相等定义,因此您可以在 的上下文中定义相等的真正含义程序

您有几种方法可以解决手头的问题。一种方法是对指针操作std::unique_ptr个对象。可以比较指针,正确使用std::function可以确保正确处理解除分配。

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

UIBarButtonItem
  

尝试通过引用存储函数,但我不知道该怎么做   那(或者如果可能的话。)

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