对函数使用擦除-删除习惯用法<void(>

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

本文关键字:lt void gt 惯用法 习惯 函数 擦除 删除      更新时间:2023-10-16

叠人。

我试图实现一个观察者(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没有。参见"为什么std::function不具有可比性?"

考虑以下情况:

假设你在main中定义了两个lambda:

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,该类型将int映射到函数对象。本例使用std::remove_if只比较int s:

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

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

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