多态与派生成员函数指针?

时间:2013-11-25 07:14:57

标签: c++ inheritance polymorphism member-function-pointers

(我看到类似的问题之前已经被问过了,但我看过的那些似乎并没有完全触及我的用例。特别是,我想知道我的编译失败是由于错误造成的,或者是我尝试使用verboten的结果。)

背景

我希望实现委托模式,用于事件处理。我想也许我需要的最好的方法是成员函数指针的映射,由std :: string索引(代表事件类型。)

我开始尝试使用std::function完成此操作,但遇到了一些问题,然后决定尝试使用原始MFP。 (我仍然愿意考虑std::function,我会接受一个答案,说明如何使用这种方法来完成我的确切需求。但我仍然想知道我目前的方法有什么问题。)< / p>

我能够使用单个课程来完成这项工作。但是我实际上希望委托映射由抽象基类提供,然后让派生类将其委托注册到该映射中。除非我在下面的代码中犯了一些错误,否则这似乎是不可能的;似乎成员函数指针不能是多态的。

我得到的编译错误如下:

mfp5.cpp: In constructor ‘Derived::Derived()’:
mfp5.cpp:41:21: error: cannot convert ‘int (Derived::*)(const Base::EventContext&)’ to ‘std::map<std::basic_string<char>, int (Base::*)(const Base::EventContext&)>::mapped_type {aka int (Base::*)(const Base::EventContext&)}’ in assignment
_delegates["foo"] = &Derived::FooEventHandler;

问题

  1. 我在下面的代码中犯了错误,还是真的不允许?简而言之,我有一个Base::*的std :: map,我想在其中插入一些Derived::*
  2. 是否有其他推荐方法可以实现此目的?
  3. 代码

    class Base
    {
      public:
        struct EventContext
        {
          int data1;
        };
        Base() {}
        virtual int ProcessEvent(std::string event, EventContext ctx) =0;
    
      protected:
        typedef int (Base::* EventHandler)(const EventContext& context);
        typedef std::map<std::string, EventHandler> EventDelegateMap;
        EventDelegateMap _delegates;
    };
    

    class Derived: Base
    {
      public:
        Derived();
        int ProcessEvent(std::string event, EventContext ctx);
    
      private:
        int FooEventHandler(const EventContext& context);
        int BarEventHandler(const EventContext& context);
        int QuxEventHandler(const EventContext& context);
    };
    
    Derived::Derived() :Base()
    {
      _delegates["foo"] = &Derived::FooEventHandler;  // error
      _delegates["bar"] = &Derived::BarEventHandler;  // error
      _delegates["qux"] = &Derived::QuxEventHandler;  // error
    }
    

3 个答案:

答案 0 :(得分:4)

似乎你想使用std::function,我想说的是:

class Base
{
  public:
    struct EventContext
    {
      int data1;
    };
    Base() {}
    virtual int ProcessEvent(std::string event, EventContext ctx) =0;

  protected:
    typedef std::function<int(const EventContext&)> HandlerType;
    typedef std::map<std::string, HandlerType> EventDelegateMap;
    EventDelegateMap _delegates;
};

class Derived: Base
{
  public:
    Derived();
    int ProcessEvent(std::string event, EventContext ctx){ return 0; }

  private:
    int FooEventHandler(const EventContext& context){ return 0; }
    int BarEventHandler(const EventContext& context){ return 0; }
    int QuxEventHandler(const EventContext& context){ return 0; }
};

Derived::Derived() :Base()
{
    auto self = this; // Some gcc versions cannot capture this correctly.
    _delegates["foo"] = [=](const EventContext& context) { return self->FooEventHandler(context); };
    _delegates["bar"] = [=](const EventContext& context) { return self->BarEventHandler(context); };
    _delegates["qux"] = [=](const EventContext& context) { return self->QuxEventHandler(context); };
}

应该工作......

编辑:正如@Joachim在评论中提到的那样,您也可以使用std::bind()生成所需的std::function对象,例如。

_delegates["foo"] = std::bind(&Derived::FooEventHandler, this, std::placeholders::_1);

我使用lambda来表明实际上,你可以在lambda中实现整个逻辑。这种方法的主要优点是,如果你实现更多的处理程序,那就更省力了,我总是赞成减少工作量......:)

答案 1 :(得分:3)

我认为这是一个类型错误,因为如果允许分配,你可以这样做:

/* This step is iffy... */
void (Base::* basePtr)() = &Derived::someMethod;

/* ... because of this. */
Base b;
(b.*basePtr)(); // Ooops...

这里,在最后一行,我们调用basePtr对象Baseb指向的函数。但这是一个问题,因为接收器对象应该具有类型Derived

希望这有帮助!

答案 2 :(得分:2)

多态函数的输入参数,例如传递给成员函数指针的this指针,只能是逆变的。由于this也是输出参数,输出参数只能是协变的,this必须是不变的,但该部分与您的问题无关。这些陈述直接来自Liskov Substitution Principle

基本上,您无法执行您尝试执行的任务,因为编译器无法证明派生类型的成员函数始终在派生对象上调用。

相关问题