通过基类虚函数获取派生类型

Get derived type via base class virtual function

本文关键字:获取 派生 类型 函数 基类      更新时间:2023-10-16

我正在尝试通过基类虚函数获取对象的派生类型。我已经写了这个,它没有编译:

struct base {
  virtual base& get_this() {
    return *this;
  }
};
struct derived : base {
  virtual derived& get_this() override {
    return *this;
  }
  void fn();
};

int main () {
  base* pd = new derived();
  derived& x = pd->get_this(); /*ERROR*/
  x.fn();
  return 0;
}

。给我一个错误:我无法从base初始化derived&。既然get_this是虚拟的,为什么pd->get_this()返回base&而不是derived&?提前感谢!

编辑:

感谢大家的有用回答,并为我迟到的回复道歉。我应该在原始帖子中指定我也对解决我的问题感兴趣,而不仅仅是弄清楚为什么上述内容无法编译。我的主要问题是fnderived类独有的,不能通过基类调用。使用强制转换确实可以解决问题,但我讨厌使用 if 其他构造编写代码只是为了获得正确的类型(斯科特迈耶斯也建议不要使用 cast:))。答案似乎表明,演员表是要走的路,这至少在某种程度上令人放心,我没有忽视一个更"优雅"的解决方案来解决我的问题。再次感谢!

C++协变返回类型支持将只起作用,只要您已经知道派生类型。若要将基类向下转换为可能的派生类,只需使用 dynamic_cast<derived>(base_ref) 来确定base_ref是否与实际派生类型匹配:

int main () {
    base* pd = new derived();
    derived& x = dynamic_cast<derived&>(*pd); // Will throw an exception if pd 
                                          // isn't a 'derived'
    x.fn();
    return 0;
}

或者:

int main () {
    base* pd = new derived();
    derived* x = dynamic_cast<derived*>(pd); // Will return nullptr if pd isn't
                                         // a 'derived'
    if(x) {
        x->fn();
    }
    else {
        // dynamic_cast<derived*> failed ...
    }
    return 0;
}

C++支持派生类的协变返回类型,但正如其他答案所描述的那样,您无法通过在此处调用基类(pd->get_this())来获取它。

如果您无法使用 RTTI、异常处理或想要严格的类型绑定(没有 vtable 开销),则还可以考虑静态多态性,以便在编译时检查类型合规性。

pd的静态类型是 base * 。因此,当编译器get_this()查找成员函数时,它只找到base::get_this()base::get_this()的返回类型是 base& ,不能转换为 derived& 。因此错误。

我想

通过参考工作C++草案的第 10.3 节第 8 段来补充 Novelocrat 的回答(单击此处),该部分解释了在这种情况下返回的指针的静态类型是 Derived* 而不是 Base*。基本上,如果您通过指向派生类的指针调用get_this(),那么您将获得正确的类型,而不会发生编译器错误。

以下是标准的引用以及示例(也来自标准):

如果返回类型 D::f

与返回类型 B::f 不同,则 返回类型 D::f 中的类类型应在该点完成 声明 D::f 或应为类类型 D。当 覆盖函数被称为被覆盖的最终覆盖器 函数,其结果转换为由 (静态选择)覆盖函数 (5.2.2)。[示例:

class B { };
class D : private B { friend class Derived; };
struct Base {
    virtual void vf1();
    virtual void vf2();
    virtual void vf3();
    virtual B* vf4();
    virtual B* vf5();
    void f();
};
struct No_good : public Base {
    D* vf4(); // error: B (base class of D) inaccessible
};
class A;
struct Derived : public Base {
    void vf1(); // virtual and overrides Base::vf1()
    void vf2(int); // not virtual, hides Base::vf2()
    char vf3(); // error: invalid difference in return type only
    D* vf4(); // OK: returns pointer to derived class
    A* vf5(); // error: returns pointer to incomplete class
    void f();
};
void g() {
    Derived d;
    Base* bp = &d; // standard conversion:
    // Derived* to Base*
    bp->vf1(); // calls Derived::vf1()
    bp->vf2(); // calls Base::vf2()
    bp->f(); // calls Base::f() (not virtual)
    B* p = bp->vf4(); // calls Derived::pf() and converts the
    // result to B*
    Derived* dp = &d;
    D* q = dp->vf4(); // calls Derived::pf() and does not
    // convert the result to B*
    dp->vf2(); // ill-formed: argument mismatch
}

C++支持协变返回类型。这意味着,当您通过base指针在derived对象上调用get_this()时,将调用的是派生的实现。

但是,这并不意味着打电话给base::get_this会给你一个derived&base::get_this的返回类型为 base& 。如果你想得到一个derived对象,你必须通过derived指针调用get_this(或将base&向下投射到derived&)。请注意,这就是返回类型协方差在Java,C++,D中的工作方式。

base* pbase = new base();
base* pderived = new derived();
derived* pderived2 = new derived();
base& a = pbase->get_this();        // call implementation in base, return base&
base& b = pderived->get_this();     // call implementation in derived, return base&
derived& c = pderived2->get_this(); // call implementation in derived, return derived&

我找到了一个简单的解决方案,但如果可能的话,我会让大师们评估:

class base{ 
    type = 1;
    virtual int getType() final {
        return type;
    }
}
class derived1 : public base {
    derived1(){
        type = 2;
    }
}

这样,你可以调用任何派生类的方法'int getType()'。由于类型是在构造函数上设置的,因此不存在不当行为的风险。为了增强可用性,我创建了一个预定义的"类型"。

我正在使用,但我不知道是否是麦吉维!