C++ 重写虚拟函数的返回类型不同,并且不是协变的
啊,所以及时回来了 我得到一个奇怪的错误:C++ 重写虚拟函数的返回类型不同,并且不是协变的,c++,covariance,virtual-functions,return-type,object-slicing,C++,Covariance,Virtual Functions,Return Type,Object Slicing,啊,所以及时回来了 我得到一个奇怪的错误: 'B::blah': overriding virtual function return type differs and is not covariant from 'A::blah' 以下是导致问题的代码: class A { public: class Inner { }; virtual Inner blah() = 0; }; class B : public A { public: class Inner2
'B::blah': overriding virtual function return type differs and is not covariant from 'A::blah'
以下是导致问题的代码:
class A {
public:
class Inner { };
virtual Inner blah() = 0;
};
class B : public A {
public:
class Inner2 : public Inner { };
Inner2 blah() {
return Inner2();
}
};
我查找了错误,根据,类型可以协变的方法之一是:
B::f的返回类型中的类与D::f的返回类型中的类相同,或者是D::f的返回类型中的类的明确的直接或间接基类,并且在D中可以访问
internal
和Inner2
不是这样吗?我使用微软Visual C++ 2010,如果它重要的话。
好的,多亏了John,我学会了只有指针和引用才是协变的。为什么呢?派生类可以强制转换为基类,那么为什么具有从同一事物派生的返回类型的虚拟函数不将返回类型强制转换为基类之一呢?在我的示例中,让
(A*(new B))->blah()
返回一个internal
,它实际上是一个已被转换的Inner2
。只有指针和引用可以是协变的 在所有情况下,blah
方法都必须返回一个::Inner——如果您想一想原因,它非常简单。B的一个实例很容易成为基类A的例子。现在如果有人调用A上的blah(基类应该返回哪个对象?Inner2还是Inner?如果允许您的请求,则意味着调用blah()通过基类必须执行从Inner2到Inner2的转换…不可能执行强制转换,因为调用方负责管理返回的对象(因为它不是通过指针/引用返回的,而是通过值返回的),并将在堆栈上为其保留空间。因此它只能处理Inner2或任何祖先类
所以你有一个Inner的实例,而不是Inner2…所以你没有任何优势
为什么呢
因为ISO C++标准委员会是这样裁定的!
没有根本的原因。这可以用不同的方式来完成,代价是增加一点编译器的复杂性。想象一下,您的示例是有效的 考虑以下代码:
A *a = some_function();
A::Inner inner = a->blah();
如果
a
的动态类型是B*,则a->blah()
调用a::B->blah()
,并返回aB::internal
。然后将其静默切片到a::internal
的实例中。通常,这(以及任何类型的切片)不是您想要的。我发现这是一个很好的限制。编译器在编译时必须知道运行时堆栈上声明的对象:
void f(const A & a) {
Inner inr = a.blah( );
}
inr必须是静态和动态类型“Inner”(而不是“Inner2”)才能从堆栈中分配——因此,如果blah返回Inner2,它将用于构造一个Inner,它的“2ness”将丢失(或者像K Dragon提到的那样被切分).正如您所说,a
B
可以转换为a
,但同样地,Inner2
也可以转换为Inner
。因此,当对转换为a
的B
调用blah
时,它会将Inner2
转换为Inner
。为什么不这样做呢?返回类型可以两者之间没有什么不同——你可以简单地让B实例化一个Inner2,但将其作为对Inner的引用返回,这也可以解决问题——我将更新对这一引用的答案。不,我不能这样做,因为这将返回对本地对象的引用。-1.一旦你使用B::blah
的结果,你就输入undef你可以从一个普通函数返回一个派生类,这个函数的签名表明它返回一个基,那么为什么不能对虚拟函数这样做呢?比如,Base test(){return-derived();}
可以工作。@Seth:这叫做切片,因为Base
有一个复制构造函数Base(const-Base&)
可以(不幸的是)必须接受一个常量派生的&
,但这几乎保证不是您想要的。如果您对切片没问题,那么只需在派生类中返回a::Inner
。@John no,我需要知道某个特定指针是派生的*
的调用方返回派生的*
,以及只知道它是B的类ase*
获取Base*
返回。@Seth:编译器在处理函数时不知道谁将进行调用,它不知道调用处的静态类型是Base
还是派生的
,它必须提供一个定义。如果希望函数进行切片,则返回一个internal
对象,但这不会让您根据调用位置的静态类型返回不同的内容。或者,您可以提供两个函数,一个是返回inner2
的非虚拟函数,另一个是返回internal
(将调用转发到新函数,切片并返回它)的虚拟函数。通常,这在C++中允许任何类型的切片。“好奇的家伙确实是,而且几乎总是一个bug。”它的“2”将会丢失。这似乎是代码编写者的意图。当您声明一个内部
实例时,您得到的是一个内部
实例,而不是Inner2
实例。这里有什么问题?代码编写者在问题“返回一个实际上是已抛出的Inner2的内部”中指出。它不被认为是“微小的”在C++的效率很重要的领域,它与“按值”返回的工作方式根本不兼容。@JimmyHartzell什么的效率?@JimmyHartzell这与“按值”返回的工作方式根本不兼容?