我为观察者模式实现了模板基类,
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 ++。
答案 0 :(得分:2)
IEventListener::messageArrived
被重载,因此编译器无法确定&IEventListener::messageArrived
的类型。它可以是void (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);
(仍然很难看)
另一个不完美的解决方案是从侦听器类型(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
答案 1 :(得分:0)
我对你的例子感到有点困惑,你正在打电话
emit(&IEventListener::messageArrived, message);
我认为该匹配
template <class Signal>
ActionListener<T>::void emit(Signal signal);
但是emit
的这个重载只需要一个参数,您认为&IEventListener::messageArrived
参数用于什么?
请注意,IEventListener
是类ActionListener
的模板参数,而不是emit
函数的模板参数。
当我尝试这个时它起作用:
ActionListener<IEventListener> al;
Message* message = 0;
al.emit(message);