问题
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