我收到一个奇怪的错误:
'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();
}
};
我查找了错误,根据a page I found on the Microsoft website,其中一种类型可以协变:if /:
B :: f的返回类型中的类与D :: f的返回类型中的类相同,或者是D的返回类型中类的明确的直接或间接基类: :f,可以在D
中访问
Inner
和Inner2
的情况不是这样吗?如果重要的话,我正在使用Microsoft Visual C ++ 2010。
好的,感谢John,我知道只有指针和引用才能协变。这是为什么?可以将Derived转换为Base,那么为什么具有从同一事物派生的返回类型的虚函数不会将返回类型转换为基类的类型?在我的示例中,似乎让(A*(new B))->blah()
返回Inner
,这实际上是Inner2
已经被施放。
答案 0 :(得分:17)
只有指针和引用才能协变。
答案 1 :(得分:2)
如果你要求的是允许的话,那就意味着通过基类调用blah()必须从Inner2转换到Inner ...由于调用者负责管理返回的对象,所以无法进行强制转换(因为它不是由指针/引用返回,而是由值返回)并将在堆栈上为它保留空间。所以它只能处理Inner而不是Inner2或者任何祖先类。
所以你有一个Inner的实例,而不是Inner2 ......所以你没有任何优势......
答案 2 :(得分:2)
想象一下你的例子有效。
请考虑以下代码:
A *a = some_function();
A::Inner inner = a->blah();
如果动态类型a
为B *,则a->blah()
会调用a::B->blah()
,并返回B::Inner
。然后将其静默切片到A::Inner
的实例中。一般来说,这(和任何类型的切片)不是你想要的。我发现这是一个很好的限制。
答案 3 :(得分:0)
为什么?
仅仅因为ISO C ++标准委员会就这样统治了它!
没有根本原因。它可以以不同的方式完成,但代价是额外的编译器复杂性。
答案 4 :(得分:0)
在运行时堆栈上声明的对象在编译时必须为编译器所知:
void f(const A & a) {
Inner inr = a.blah( );
}
inr必须是静态和动态类型“内部”(不是“Inner2”)才能从堆栈中分配 - 所以如果blah返回一个Inner2,它将用于构造一个内部,它的“2ness”将是丢失(或切成K龙提及)。
答案 5 :(得分:-1)
blah
方法在所有情况下都必须返回A :: Inner - 如果你想到它很简单的原因。 B的一个实例很容易成为基类A的情况。现在如果有人在A上调用blah(基类应该返回哪个对象?Inner2还是Inner?