为什么我不能将指向Derived类成员函数的指针强制转换为类Base?

时间:2012-04-15 14:21:12

标签: c++ member-function-pointers

对我来说,将void(Derived::*)()投射到void(Base::*)()看起来非常安全,就像在此代码中一样:

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

但编译器抱怨a.any_method=&Derived::a_method;的演员。这是一个阻止细微编程错误的障碍,还是一些让编译器编写者生活更轻松的东西?是否存在变通方法让Base类具有指向Derived 的成员函数的指针,而没有类型知识(也就是说,我不能使Base成为模板的模板参数Derived)。

4 个答案:

答案 0 :(得分:7)

如果您的Derived::a_method()尝试使用数据成员仅出现在Derived中,而不是Base中,并且您在Base对象上调用它(或者对象派生自Base但与Derived无关?)

相反的转换是有道理的,这个没有。

答案 1 :(得分:3)

不,这有潜在危险。

派生类函数可以使用*this的所有派生类属性。可以在任何基类实例上调用指向基类函数的指针,甚至是那些不是派生类型的实例。

访问不是派生类的实例的派生类属性是不行的,因此正确地不允许将指向派生类函数的指针强制转换为指向基类指针的指针。

另一方面,将指向基类函数的指针转换为指向派生类函数的指针是安全合法的。

答案 2 :(得分:1)

您需要使用std::function<void()>。这可以是任何类的任何成员,lambda,自由函数,函数对象,无论你需要什么,这都非常方便。

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

在这里你可以看到any_method的实际实现完全从<{1}} 抽象出来,我可以提供一个功能对象,它可以做任何事情 - 包括方便调用派生方法。

答案 3 :(得分:0)

我想这可能有点令人惊讶。但是,如果你考虑一下,这是有道理的。

对于两种类型之间的自动转换,应该保持以下关系:第一种类型的任何实例都应该在第二种类型中表示。

例如,如果dDerived的实例,那么它可以自动转换为Base&,因为Derived的任何实例也是Base的实例{1}}。这是继承。

现在,当谈到指向成员函数的指针时,实际上这种关系是相反的。很明显Base的实例存在Derived的任何方法,但反之则不然。毕竟,导出的全部内容是更频繁地添加新功能。

另一种可视化方法是使用自由函数。 this只是常规函数中的隐式参数,如果我们明确表示我们得到:

void Base@call_it(Base& self);

void Derived@a_method(Derived& self);

现在,如果我有两个d类型Derivedb类型Base的实例,那么:

  • Base@call_it(d)有道理
  • Derived@a_method(b)是编译错误

后者可能是Derived@a_method(dynamic_cast<Derived&>(b)),但这会引入运行时检查以实际验证属性。静态地说它不是可判定的。