1

オブザーバーパターンのテンプレート基本クラスを実装しました。

template<class T>
class ActionListener
{
public:
    ActionListener(void);    
    virtual ~ActionListener(void);    
    void registerListener(T* listener);    
    void unregisterListener(T* listener);

    template<typename Signal>
    void emit(Signal signal);

    template<typename Signal, typename Parameter>
    void emit(Signal signal, const Parameter& parameter);

    template<typename Signal, typename Parameter1, typename Parameter2>
    void emit(Signal signal, 
              const Parameter1& parameter1, 
              const Parameter2& parameter2);

private:
    std::vector<T*> mListenerList;
};

class IEventListener
{
public:
    virtual void messageArrived( Message* message);
    virtual void messageArrived(ClientHandle* handle, Message* message);
};

私はこのようなクラスを使用しています

emit(&IEventListener::messageArrived, message);
emit(&IEventListener::messageArrived, mHandle, message); 

ここでの問題は、コンパイラがテンプレートパラメータを推測できず、テンプレートパラメータを明示的に指定できなかったことです。

誰かがアイデアを持っていますか?

編集:ここでの問題は、テンプレートパラメータを使用した関数呼び出しのオーバーライドです。「Emit」関数は他の関数タイプで正しく機能します。

このパターンの使用法は

class SampleClass : public ActionListener<IEventListener>
{
//some stuff here
//this class is observing events of IEventListener
}

ちなみにこれはC++です。

4

2 に答える 2

2

IEventListener::messageArrivedはオーバーロードされているため、コンパイラは の型を判別できません&IEventListener::messageArrivedvoid (IEventListener::*)(Message*)またはの可能性がありますvoid (IEventListener::*)(ClientHandle*, Message*)

簡単な (そして醜い) 解決策は&IEventListener::messageArrived、次のように、呼び出しサイトで目的の型に明示的にキャストすることです。

emit(static_cast<void (IEventListener::*)(Message*)>(&IEventListener::messageArrived), a_message_ptr);

または、目的の関数タイプの変数に割り当てることによって:

void (IEventListener::*func_ptr)(Message*) = &IEventListener::messageArrived;
emit(func_ptr, a_message_ptr);

(ダサいって言った?)

テンプレート パラメータを明示的に指定することもできます。

emit<void (IEventListener::*)(Message*)>(&IEventListener::messageArrived, a_message_ptr);

(相変わらず醜い)

もう 1 つの不完全な解決策はSignal、リスナーの型 ( T) とその他のパラメーターから の型を推測することです。

// Warning: untested. 
// For illustration purposes only
template<class T>
class ActionListener
{
public:
    //...
    void emit(void (T::*signal)());

    template<class Arg1T>
    void emit(void (T::*signal)(Arg1T), Arg1T);

    template<class Arg1T, class Arg2T>
    void emit(void (T::*signal)(Arg1T, Arg2T), Arg1T, Arg2T);
};

ただし、引数の型が正確に一致する必要があるため、これは不完全です。

設計に加えることができる変更の程度に応じて、IEventListener のメンバーに異なる名前を付けてあいまいさを取り除くことが、より簡単な解決策になります。Boost.Signals2のような既存のシグナル/スロット ライブラリを使用することもできます。

于 2009-09-23T14:49:40.783 に答える
0

私はあなたの例について少し混乱しています、あなたは呼んでいます

emit(&IEventListener::messageArrived, message);

一致するはずだと思います

template <class Signal>
ActionListener<T>::void emit(Signal signal);

しかし、この のオーバーロードはemitパラメーターを 1 つしか取りません。この&IEventListener::messageArrivedパラメーターは何に使用されると思いますか?

IEventListenerこれはクラスのテンプレート パラメーターでありActionListener、関数のパラメーターではないことに注意してくださいemit

私がこれを試したとき、それは動作します:

ActionListener<IEventListener> al;
Message* message = 0;
al.emit(message);
于 2009-09-23T14:39:49.080 に答える