简体   繁体   English

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

[英]Polymorphism with derived member function pointers?

(I see that similar questions have previously been asked on SO, but the ones I've seen don't appear to touch on my use-case exactly. In particular, I'm wanting to know if my compilation failure is the result of a mistake, or the result of my attempting what is verboten.) (我看到之前已经问过类似的问题,但是我看到的那些问题似乎并没有完全触及我的用例。特别是,我想知道我的编译失败是否是由于一个错误,或者是我尝试什么是verboten的结果。)

Background 背景

I wish to implement the delegate pattern, for event handling. 我希望实现委托模式,用于事件处理。 I think perhaps the best approach for my needs is a map of member function pointers, indexed by std::string (which represent the event types.) 我想也许对我的需求最好的方法是成员函数指针的映射,由std :: string索引(表示事件类型。)

I started out trying to accomplish this with std::function , but ran into some problems, and then decided to try over with just raw MFPs. 我开始尝试使用std::function完成此操作,但遇到了一些问题,然后决定尝试使用原始MFP。 (I am still willing to consider std::function , and I will accept an answer that shows how to accomplish my exact needs below, using that approach. But I would still like to know what is wrong with my current approach.) (我仍然愿意考虑使用std::function ,我会接受一个答案,说明如何使用这种方法完成我的确切需求。但我仍然想知道我当前的方法有什么问题。)

I was able to get this working with a single class. 我能够用一个班级来完成这个工作。 However I actually want the delegate map to be provided by an abstract base class, and then have a derived class register its delegates into that map. 但是我实际上希望委托映射由抽象基类提供,然后让派生类将其委托注册到该映射中。 Unless I've made some mistake in the code below, this appears not to be possible; 除非我在下面的代码中犯了一些错误,否则这似乎是不可能的; it appears that member function pointers cannot be polymorphic. 似乎成员函数指针不能是多态的。

The compilation errors I get look like this: 我得到的编译错误如下:

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;

Questions 问题

  1. Did I make a mistake in the code below, or is this truly disallowed? 我在下面的代码中犯了错误,还是真的不允许? In a nutshell, I have a std::map of Base::* , and I want to insert some Derived::* into it. 简而言之,我有一个Base::*的std :: map,我想在其中插入一些Derived::*
  2. Is there some other recommended method to accomplish this? 是否有其他推荐的方法来实现这一目标?

Code

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
}

It seems you want to use std::function , I would say something like: 看来你想使用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); };
}

Ought to work... 应该工作......

EDIT: As @Joachim mentions in his comment, you can use std::bind() to generate the required std::function object too, eg 编辑:正如@Joachim在他的评论中提到的,你可以使用std::bind()来生成所需的std::function对象,例如

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

I used the lambda to show that in reality, you can implement the entire logic in the lambda. 我使用lambda来表明实际上,你可以在lambda中实现整个逻辑。 The primary advantage of this approach is that if you are implementing more handlers, it's less effort and I'm always in favour of less effort... :) 这种方法的主要优点是,如果你实现更多的处理程序,它的努力就会减少,而且我总是赞成减少工作量...... :)

I believe that this is a type error because if the assignment were allowed, you could do something like this: 我认为这是一个类型错误,因为如果允许赋值,你可以这样做:

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

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

Here, in the last line, we call the function pointed at by basePtr inside the Base object b . 这里,在最后一行中,我们在Base对象b调用basePtr指向的函数。 But that's a problem, since the receiver object is supposed to have type Derived ! 但这是一个问题,因为接收器对象应该具有Derived类型!

Hope this helps! 希望这可以帮助!

Input arguments to polymorphic function, eg, the this pointer passed to a member function pointer, can only be contravariant. 多态函数的输入参数,例如,传递给成员函数指针的this指针,只能是逆变的。 Since this is also an output argument and output arguments can only be covariant, this has to be invariant but that part has nothing to do with your question. 由于this也是输出参数,输出参数只能是协变的, this必须是不变的,但该部分与您的问题无关。 These statements derive immediately from the Liskov Substitution Principle . 这些陈述立即来自Liskov替代原则

Basically, you cannot do the assignment you are attempting to do because the compiler can't prove that your member function of a derived type is always called on a derived object. 基本上,您无法执行您尝试执行的任务,因为编译器无法证明派生类型的成员函数始终在派生对象上调用。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM