我试图通过基类虚函数获取对象的派生类型。我写了这个,但没有编译:
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&
?提前谢谢!
编辑:
感谢大家为我迟到的回复所提供的有用答案和道歉。我应该在原帖中指出我也对我的问题的解决方案感兴趣,而不仅仅是弄清楚上面为什么不编译。我的主要问题是fn
对derived
类是唯一的,不能通过基类调用。使用强制转换肯定解决了问题,但我讨厌用if else构造编写代码以获得正确的类型(也是Scott Meyers建议反对强制转换:))。答案似乎表明演员阵容是要走的路,这在某种程度上至少让人放心,我不会忽视对我的问题更“优雅”的解决方案。再次感谢!
答案 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;
}
c++支持派生类的协变返回类型,但正如其他答案描述的那样,你不能通过调用基类(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是不是!