Использование функции удаления-удаления для функции & lt; void () & gt;

Сложенные люди.

Я пытаюсь реализовать шаблон наблюдателя (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;
};

2

Решение

Ваша проблема может быть сведена к тому, что два std::function экземпляры нельзя сравнивать на равенство. std::remove требует operator==, а также std::function не имеет его Увидеть «Почему std :: function не сравнимо по равенству?».

Рассмотрим следующую ситуацию.

Допустим, вы определили две лямбды в вашем main:

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

Теперь, эти два равны или нет? Они делают то же самое, но, возможно, это чистое совпадение. Станут ли они неравными, если второй "Hello" стал "Hello2"? Станут ли они неравными, если второй не будет больше лямбда, а реальная функция void f()?

Дело в том, что не может быть в общем-то полезное определение равенства для функциональных объектов, так что вам решать, что на самом деле означает равенство в контексте ваш программа.

У вас есть несколько вариантов решения проблемы. Один будет работать на указатели в std::function объекты. Указатели можно сравнивать и правильно использовать std::unique_ptr гарантирует, что освобождение обрабатывается правильно.

Или вы назначаете идентификатор каждому std::function ты используешь. Смотрите следующий модифицированный пример вашего кода, в котором прямое хранение std::function<void()> в векторе заменяется пользовательским типом EventFunction который отображает 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);
};

Пробовал хранить функции по ссылке, но я не уверен, как это сделать
это (или если это возможно.)

Это невозможно, потому что вы не можете хранить ссылки в контейнерах стандартной библиотеки. Но я полагаю, что идея похожа на ту, что содержит указатели, о которых я упоминал выше.

3

Другие решения

Других решений пока нет …

По вопросам рекламы [email protected]