9

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

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&从 a 初始化 a base。既然get_this是虚拟的,为什么要pd->get_this()返回 abase&而不是 a derived&?提前致谢!

编辑:

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

4

5 回答 5

8

只要您已经知道派生类型,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;
}

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

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

于 2013-10-21T19:27:37.683 回答
3

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

于 2013-10-21T18:28:43.940 回答
1

我想通过向您推荐工作草案 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
}
于 2013-10-21T18:56:42.790 回答
1

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

然而,这并不意味着调用base::get_this会给你一个derived&. 的返回类型base::get_thisbase&。如果你想获得一个derived对象,你将不得不get_this通过一个derived指针调用(或向下转换base&为 a 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&
于 2013-10-21T19:02:47.413 回答
-2

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

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

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

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

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

于 2015-08-17T01:38:21.043 回答