【问题标题】:Overridden function pointer problem at template base class C++模板基类 C++ 中的重写函数指针问题
【发布时间】:2009-09-23 13:56:02
【问题描述】:

我为观察者模式实现了一个模板基类,

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++。

【问题讨论】:

  • 为什么不能明确给出模板参数类型?
  • "...编译器无法推导出模板参数..." 错误信息是什么?
  • 我无法理解您要做什么。请添加更多解释。您省略了 Message 的定义,也没有提到 ActionListener 应该如何与 IEventListener 交互。您的代码片段并不完全不言自明。我认为您尝试在这里模拟 Java 风格的泛型,其中 T 是派生自 IEventListener 的类类型。但我不确定。

标签: c++ design-patterns inheritance templates function-pointers


【解决方案1】:

IEventListener::messageArrived 被重载,因此编译器无法确定&amp;IEventListener::messageArrived 的类型。可能是void (IEventListener::*)(Message*)void (IEventListener::*)(ClientHandle*, Message*)

直截了当(且丑陋)的解决方案是在调用站点将 &amp;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);

(还是丑)

另一个不完美的解决方案是从监听器的类型(T)和其他参数推导出Signal的类型:

// 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

【讨论】:

  • 你能在这里添加显式转换吗,因为我不能为所有人做 cast.thx。
  • 我添加了通过强制转换、使用中间变量或显式声明模板参数来显式指定类型的示例。
  • 我还在答案末尾添加了一段其他建议。
【解决方案2】:

我对你的例子有点困惑,你在打电话

emit(&IEventListener::messageArrived, message);

我认为应该匹配

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

但是emit这个重载只需要一个参数,你觉得&amp;IEventListener::messageArrived这个参数是干什么用的?

请记住,IEventListenerActionListener 类的模板参数,而不是 emit 函数的模板参数。

当我尝试这个时它起作用了:

ActionListener<IEventListener> al;
Message* message = 0;
al.emit(message);

【讨论】:

  • 我添加了一些解释。Message或Clienthandle类无需解释,它们只是示例类。
猜你喜欢
  • 2019-03-23
  • 1970-01-01
  • 2010-10-26
  • 1970-01-01
  • 1970-01-01
  • 2014-09-04
  • 1970-01-01
  • 2016-01-07
  • 2011-12-09
相关资源
最近更新 更多