Polymorphism with pointers to derived member functions?

(I see that similar questions were previously asked on SO, but the ones I saw do not seem to affect my use case. In particular, I want to know if my compilation failure is the result of an error or the result of my attempts to is verboten.)

Background

I want to implement a delegate pattern for handling events. I think that perhaps the best approach for my needs is a map of member function pointers indexed by std :: string (which represent event types.)

I started trying to accomplish this with std::function , but ran into some problems, and then decided to try using only raw MFPs. (I'm still ready to consider std::function , and I will accept an answer that shows how to fulfill my exact needs below using this approach. But I still would like to know what is wrong with my current approach.)

I managed to get this to work with one class. However, in reality, I want the delegate map to be provided by an abstract base class, and then have a derived class registering its delegates on that map. If I did not make a mistake in the code below, this seems impossible; it looks like member function pointers cannot be polymorphic.

The compilation errors that 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

  • Have I made a mistake in the code below, or is it really forbidden? In a nutshell, I have std :: map Base::* , and I want to insert several Derived::* into it.
  • Is there any other recommended method for this?

the 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 } 
+7
c ++ inheritance polymorphism member-function-pointers
source share
3 answers

It seems you want to use std::function , I would say something like:

 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); }; } 

Must work...

EDIT: As @Joachim notes in his comment, you can use std::bind() to create the required std::function object, for example.

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

I used lambda to show that you can actually implement all the logic in lambda. The main advantage of this approach is that if you execute more handlers, this is less effort, and I always favor less effort ... :)

+4
source share

I believe this is a type error, because if assignment is allowed, you can 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 specified by basePtr inside the Base b object. But this problem, since it is assumed that the receiver object is of type Derived !

Hope this helps!

+3
source share

Entering arguments into a polymorphic function, for example, the this pointer, passed to the pointer to a member function, can only be contravariant. Since this also an output argument, and the output arguments can only be covariant, this should be invariant, but this part has nothing to do with your question. These statements are derived directly from the Liskov Principle of Replacement .

Basically, you cannot fulfill the assignment that you are trying to do, because the compiler cannot prove that your member function of a derived type is always called on a derived object.

+2
source share

All Articles