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

时间:2013-10-21 18:20:16

标签: c++ types derived

我试图通过基类虚函数获取对象的派生类型。我写了这个,但没有编译:

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;
}

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

编辑:

感谢大家为我迟到的回复所提供的有用答案和道歉。我应该在原帖中指出我也对我的问题的解决方案感兴趣,而不仅仅是弄清楚上面为什么不编译。我的主要问题是fnderived类是唯一的,不能通过基类调用。使用强制转换肯定解决了问题,但我讨厌用if else构造编写代码以获得正确的类型(也是Scott Meyers建议反对强制转换:))。答案似乎表明演员阵容是要走的路,这在某种程度上至少让人放心,我不会忽视对我的问题更“优雅”的解决方案。再次感谢!

5 个答案:

答案 0 :(得分:7)

C++ covariant return types支持只有在你已经知道派生类型的情况下才有效。要将downcast基类转换为可能派生的类,只需使用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;
}

支持派生类的协变返回类型,但正如其他答案描述的那样,你不能通过调用基类(pd->get_this())来获取它。

如果你不能使用static polymorphism,异常处理或想要紧密类型绑定(没有vtable开销),你也可以考虑RTTI在编译时检查类型的合规性。

答案 1 :(得分:3)

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

答案 2 :(得分:1)

我想通过引用您working draft C++ standard (click here)的第10.3节第8段来补充Novelocrat的答案,它解释了在哪种情况下返回指针的静态类型是Derived *而不是Base *。基本上,如果您通过指向dervied类的指针调用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
}

答案 3 :(得分:1)

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

但是,这并不意味着调用base::get_this会给你一个derived&base::get_this的返回类型为base&。如果您想获得derived个对象,则必须通过get_this指针致电derived(或将您的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&

答案 4 :(得分:0)

我找到了一个简单的解决方案,但如果可行,我会让主人评估:

class base{ 
    type = 1;
    virtual int getType() final {
        return type;
    }
}

class derived1 : public base {
    derived1(){
        type = 2;
    }
}

这样,您可以调用方法&#39; int getType()&#39;任何派生类。由于在构造函数上设置了类型,因此不存在不当行为的风险。 为了增强可用性,我创建了预定义的“类型”。

我正在使用,但我不知道MacGyvery是不是!