9

啊,SO及时回来了。

我收到一个奇怪的错误:

 '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();
    }
};

我查找了错误,根据我在 Microsoft 网站上找到的页面,类型可以协变的一种方式是:

B::f 的返回类型中的类与 D::f 的返回类型中的类相同,或者,是 D::f 的返回类型中的类的明确直接或间接基类,并且可在 D 中访问

Inner和不是这种情况Inner2吗?如果重要的话,我正在使用 Microsoft Visual C++ 2010。


好的,多亏了 John,我才知道只有指针和引用可以是协变的。这是为什么?Derived 可以强制转换为基类,那么为什么具有从同一事物派生的返回类型的虚拟函数不将返回类型强制转换为基类之一呢?(A*(new B))->blah()在我的示例中,返回 a似乎是有意义的Inner,它实际上是一个Inner2已经被抛出的。

4

6 回答 6

18

只有指针和引用可以是协变的。

于 2011-08-06T21:29:37.857 回答
3

想象一下,您的示例有效。

考虑以下代码:

A *a = some_function();
A::Inner inner = a->blah();

如果 的动态类型a为 B*,则a->blah()调用a::B->blah(),并返回 a B::Inner。然后它被默默地分割成一个A::Inner. 一般来说,这(以及任何类型的切片)都不是您想要的。我觉得这是一个很好的限制。

于 2013-05-28T10:00:32.463 回答
2

如果允许您的请求,则意味着通过基类对 blah() 的调用必须进行从 Inner2 到 Inner 的转换......由于调用者负责管理返回的对象(因为它不是由指针/引用返回,而是由值返回),并将在堆栈上为其保留空间。所以它只能处理 Inner 而不是 Inner2 或任何祖先类。

所以你有一个 Inner 的实例,而不是 Inner2 ......所以你没有任何优势......

于 2011-08-06T22:19:53.350 回答
0

这是为什么?

仅仅是因为 ISO C++ 标准委员会是这样统治的!

没有根本原因。它可以以不同的方式完成,代价是稍微增加编译器的复杂性。

于 2013-05-27T18:47:14.227 回答
0

在运行时堆栈上声明的对象必须在编译时为编译器所知:

void f(const A & a) {
   Inner inr = a.blah( );

}

inr 必须是静态和动态类型“Inner”(不是“Inner2”)才能从堆栈中分配——所以如果 blah 返回一个 Inner2 它将用于构造一个 Inner 并且它的“2ness”将丢失(或正如 K Dragon 所提到的那样切片)。

于 2013-07-29T19:00:43.457 回答
-1

在所有情况下,该blah方法都必须返回 A::Inner ——如果您想到原因,这很简单。B 的实例很容易成为基类 A 的案例。现在,如果有人在 A 上调用 blah(它应该返回哪个对象的基类?Inner2 还是 Inner?

于 2011-08-06T21:39:50.460 回答