cast a pointer to member function in derived class to a pointer to abstract member function

时光毁灭记忆、已成空白 提交于 2021-02-08 06:42:30

问题


I'm trying to do something that seems like it should be fairly common but I've been unable to find anyone discussing it. this post on stackoverflow is similar to what I'm trying to do, but not quite the same.

I have an abstract base class:

#ifndef _ABASECLASS_H_
#define _ABASECLASS_H_

using namespace std;

#include <iostream>

#define CALL_MBR_FUNC(object, ptr_to_mem_func) ((object).*(ptr_to_mem_func))

class aBaseClass
{
public:

  typedef void (aBaseClass::*aBaseClass_mem_func)();

  int A;
  int B;

  aBaseClass();

  aBaseClass(int a, int b);

  virtual void function1(aBaseClass_mem_func infunc) = 0; 

  virtual void function2() = 0; 

};

#endif /* _ACLASS_H_ */

and I have a derived class:

#ifndef _ASUBCLASS_H_
#define _ASUBCLASS_H_

using namespace std;

#include <iostream>
#include "aBaseClass.h"


/* A simple class containing two ints and some functions to demonstrate passing via various methods.  It is a subclass of aClass*/
class aSubClass: public aBaseClass
{
public:

  aSubClass();

  aSubClass(int a, int b);

  void function1(aBaseClass_mem_func infunc); 

  void function2(void); 

};

#endif /* _ASUBCLASS_H_ */

where function1 and function2 are:

void aSubClass::function1(aBaseClass_mem_func infunc) 
{
  CALL_MBR_FUNC(*this, infunc)();
}


void aSubClass::function2(void) 
{
  A = 42;
  B = 66;
}

Finally, in the main() I try to call function1 targeted on an object of type aSubClass, passing a pointer to function2 in aSubClass:

int main (int argc, const char * argv[])
{
  aSubClass eh(2,5);
// This doesn't work    
  aBaseClass_mem_func trythis = &aSubClass::function2;
// This also doesn't work
  eh.function1(&aSubClass::function2);

  return(0);
}

OK, we can automatically cast a pointer-to-derived type to a pointer-to-base type. I have now read that we can't pass a pointer-to-derived-member-function to a pointer-to-base-member-function. I think I understand why (the derived member function might make use of things that exist in the derived class but don't exist in the base class).

But I'm trying to build a library of two categories of classes (derived from two base classes). Call them baseclass1 and baseclass2. One of the member functions in any derived class from baseclass1 needs to be able to be handed a particular member function from any derived class from baseclass2. Is there some trick I can use to carry out the necessary cast? Do I have to use the explicit keyword and define the cast somehow?


回答1:


You could shorten this example a lot:

struct B {
    virtual void foo() = 0;
};

struct D : B {
    void foo() override { }
};

int main() {
    void (B::*ptr)() = &D::foo; // error: cannot initialize a variable of
                                // type 'void (B::*)()' with an rvalue of type 
                                // 'void (D::*)()': different classes ('B' vs 'D')
}

The error message, at least on clang, is pretty clear. gcc just says cannot initialize. The issue is just that you cannot implicitly convert a pointer-to-derived-member to a pointer-to-base-member. But you can do it explicitly with static_cast:

void (B::*ptr)() = 
    static_cast<void (B::*)()>(&D::foo); // ok!

Side-note: please remove the CALL_MBR_FUNC macro from your code and never write such a thing ever again.




回答2:


Why it doesn't work:

One way you can think of a member function is this:

struct Foo {
    void go () { }
} ;

Could also be expressed as:

void go ( Foo* this ) { }

So, this:

typedef void(Foo::*MemberFunctionPtr)() ;

Is kind of like this:

typedef void(*MemberFunctionPtrForFoo)(Foo*) ;

However, if you have a subclass like this:

struct Bar : public Foo {
    void go2 () { }
} ;

That function is also kind of like this:

void go2 ( Bar* this ) { }

So when you take the address of Bar::go2, you're getting basically a pointer to a function that looks like void go2 ( Bar* this ). Why is this a problem?

Well let's look at what this means...

If you had this function:

void function ( Foo * this ) ;

And you were to do this:

Bar * bar = new Bar () ;
function ( bar ) ;

This would work (as it should). C++ kindly made it possible for you then to be able to do things like this:

void(*functionPtr)(Bar*) = &Foo::go ;

However, lets say you instead had this function:

void function ( Bar * this ) ;

And you did this:

Foo * foo = new Foo() ;
function ( foo ) ;

This wouldn't work because foo isn't [necessarily] a Bar. You could static_cast that, which is your way of telling the compiler "no, really, I'm pretty sure I know what I'm doing" (as opposed to reinterpret_cast, which is your way of telling the compiler "you're stupid; I know what I'm doing.")

Therefore, it also won't let you cast the member functions.

Another answer said that static_cast can convert the member functions, but that's only because static_cast is allowed to do the reverse of implicit cast (except for cv-qualification). You can do it, but it has the same caveats.

Disclaimer: this is a fairly simplified version of the spec, but it gets the point across.

A better solution in most cases:

On to a [potentially] better solution [unless absolute performance is key]: boost::function (or, as of C++11 std::function). This is a "functor".

Your member function could instead be written:

class Foo {
    void function ( boost::function<void()> function ) { }
} ;

The functor object can be constructed with anything that can be called with the specified prototype (in this case, something taking no arguments and returning void). You can pass the address of a C-function for instance.

Another thing you can do is "bind" functions (which basically grabs arguments and makes a function). There's boost::bind for this.

For example you could do this:

Foo foo ;
Bar bar ;
foo.function ( boost::bind(&Bar::go2,&bar) ) ;

The boost bind takes some function as the first argument. If the function is a member function, the next argument must either be an instance of a class that the specified method can be called on (in which case it's copied) or a pointer to a class the specified method can be called on (in which case it's referenced). This example would actually cause the foo instance to call the bar instance (instead of itself), but you could pass &foo instead.

You can even be more creative:

class Foo {
    void function ( boost::function<void(int)> function ) {
        function ( 1 ) ;
    }
    void go2 ( int a , int b ) {
        cout << a << " " << b << endl ;
    }
} ;
Foo foo ;
foo.function ( boost::bind(&Foo::go2,&foo,_1,2) ) ;

That bind grabs:

  • The member-function-pointer for Foo::go2
  • A reference (or, 'pointer') to that instance of foo
  • A placeholder for "the first argument of the resultant function," which'll become the first argument in the call to go2
  • The number 2, which'll become the second argument in the call to go2

This is what'll be printed to the console:

1 2

This is an extraordinarily powerful tool and will lead you into the fantastic world of functional programming while making your life easier along the way. (It'll also make people like @CortAmmon hate you.)



来源:https://stackoverflow.com/questions/31601217/cast-a-pointer-to-member-function-in-derived-class-to-a-pointer-to-abstract-memb

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!