2

積み重なった人々。

私のプログラムにオブザーバー(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;
};
4

1 に答える 1

3

問題は、2 つのstd::functionインスタンスが等しいかどうかを比較できないという事実に帰着できます。std::removeが必要ですがoperator==std::function持っていません。「std::function が等値比較できないのはなぜですか?」を参照してください。.

次の状況を考えてみましょう。

で 2 つのラムダを定義したとしますmain

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

さて、それらの 2 つは等しいかどうか。彼らは同じことをしますが、おそらくこれはまったくの偶然です. 2番目が になったら、それらは不平等に"Hello"なり"Hello2"ますか? 2 番目の関数がラムダ関数ではなく実際の関数である場合、それらは等しくなくなりますvoid f()か?

問題は、関数オブジェクトの等価性の一般的に有用な定義が存在しない可能性があることです。そのため、プログラムのコンテキストで等価性が実際に何を意味するかを定義するのはあなた次第です

目の前の問題を解決するには、いくつかのオプションがあります。1 つは、オブジェクトへのポインターを操作することです。std::functionポインターを比較することができ、適切に使用std::unique_ptrすることで解放が正しく処理されます。

または、使用するすべてに識別子を割り当てstd::functionます。次の変更されたコードの例を参照してください。この例では、ベクトル内の の直接格納が、関数オブジェクトにマップするstd::function<void()>カスタム型に置き換えられています。この例では、 sのみを比較するために使用します。EventFunctionintstd::remove_ifint

#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 に答える