why can't I cast a pointer to Derived class member function to the same but of class Base?

冷暖自知 提交于 2019-12-17 16:48:21

问题


To me it looks perfectly safe to cast a void(Derived::*)() to a void(Base::*)(), like in this code:

#include <iostream>
#include <typeinfo>
using namespace std;
struct Base{
    void(Base::*any_method)();
    void call_it(){
        (this->*any_method)();
    }
};
struct Derived: public Base{
    void a_method(){
        cout<<"method!"<<endl;
    }
};
int main(){
    Base& a=*new Derived;
    a.any_method=&Derived::a_method;
    a.call_it();
}

But the compiler complains about the cast at a.any_method=&Derived::a_method;. Is this a roadblock to prevent subtle programming errors, or just something to make life easier for compiler writers? Are there workarounds to let the Base class have a pointer to member functions of Derived without type knoweledge (that is, I cannot make Base a template with template argument Derived).


回答1:


What happens if your Derived::a_method() attempts to use a data member only present in Derived, not in Base, and you call it on a Base object (or an object derived from Base but not related to Derived)?

The conversion the other way around makes sense, this one doesn't.




回答2:


No, it's potentially dangerous.

A derived class function can use all the derived class properties of *this. A pointer to base class function can be called on any base class instance, even those that are not of the derived type.

Accessing the derived class properties of an instance that isn't a derived class isn't going to work so casting a pointer to derived class function to a pointer to base class pointer is correctly not allowed.

On the other hand, casting a pointer to base class function to a pointer to derived class function is safe and legal.




回答3:


You need to use a std::function<void()>. This can be any member of any class, a lambda, a free function, a function object, whatever you need, which is super convenient.

#include <iostream>
#include <typeinfo>
using namespace std;
struct Base{
    std::function<void()> any_method;
    void call_it(){
        any_method();
    }
};
struct Derived: public Base{
    void a_method(){
        cout<<"method!"<<endl;
    }
};
int main(){
    Derived* d = new Derived;
    Base& a= *d;
    a.any_method = [d] { d->a_method(); };
    a.call_it();
}

Here you can see that the actual implementation of any_method is totally abstracted from struct Base, and I can supply a function object that does anything, at all- including conveniently calling a Derived method.




回答4:


I imagine this can be somewhat surprising. However it makes sense if you think about it.

For a cast between two types to be automatic, the following relation should hold: any instance of the first type should be representable in the second.

For example, if d is an instance of Derived, then it can be automatically cast as a Base& because any instance of Derived is also an instance of Base. This is inheritance.

Now, when it comes to pointer to member-functions, the relation is actually reversed. It will seem obvious that any method of Base exists for an instance of Derived but the reverse is not true. After all the whole point of deriving is to add new functionality more often that note.

Another way of visualizing this is to use free-functions. this is just an implicit parameter in regular function, if we make it explicit we get:

void Base@call_it(Base& self);

void Derived@a_method(Derived& self);

Now, if I have two instances d of type Derived and b of type Base then:

  • Base@call_it(d) makes sense
  • Derived@a_method(b) is a compilation error

The latter could be Derived@a_method(dynamic_cast<Derived&>(b)), but this introduces a runtime check to actually verify the property. Statically it is not decidable.



来源:https://stackoverflow.com/questions/10162823/why-cant-i-cast-a-pointer-to-derived-class-member-function-to-the-same-but-of-c

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