2

Stacked people.

Iam trying to implement an observer(esque?) pattern for my program. I have a component which stores what functions should be called if an event occours. My prolem is that i dont know how should i erase my function from the container, if the need arises. Tried storing the functions by reference, but iam not sure how to do that(or if thats possible.)

#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;
};
4

1 回答 1

3

您的问题可以简化为std::function无法比较两个实例是否相等的事实。std::remove需要operator==,并且std::function没有。请参阅“为什么 std::function 不能相等比较?” .

考虑以下情况。

假设您在 中定义了两个 lambda main

auto fn = [](){cout << "Hello.\n"; };
auto fn2 = [](){cout << "Hello.\n"; };

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

问题是对于函数对象没有普遍有用的相等定义,因此由您来定义相等在程序上下文中的真正含义

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

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

尝试通过引用存储函数,但我不确定如何做到这一点(或者如果可能的话。)

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

于 2015-10-03T12:52:21.003 回答