重写虚函数返回类型不同并且不是协变的

发布于 11-28 06:46 字数 939 浏览 1 评论 0原文

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

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

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

InnerInner2 不是这样吗?如果有必要的话,我正在使用 Microsoft Visual C++ 2010。


好的,感谢约翰,我了解到只有指针和引用可以是协变的。这是为什么? Derived 可以强制转换为 Base,那么为什么具有从同一事物派生的返回类型的虚拟函数不将返回类型强制转换为基类之一呢?在我的示例中,让 (A*(new B))->blah() 返回一个实际上是 Inner 似乎是有意义的>Inner2 已被铸造。

Ah, SO came back just in time.

I am getting a strange error:

 'B::blah': overriding virtual function return type differs and is not covariant from 'A::blah'

Here is the code causing the problem:

class A {
public:
    class Inner { };

    virtual Inner blah() = 0;
};

class B : public A {
public:
    class Inner2 : public Inner { };

    Inner2 blah() {
        return Inner2();
    }
};

I looked up the error, and according to a page I found on the Microsoft website, one of the ways types can be covariant is if:

the class in the return type of B::f is the same class as the class in the return type of D::f or, is an unambiguous direct or indirect base class of the class in the return type of D::f and is accessible in D

Is that not the case with Inner and Inner2? I am using Microsoft Visual C++ 2010 if it matters.


OK, so thanks to John I learned that only pointers and references can be covariant. Why is that? A Derived can be cast to a Base, so why don't virtual functions with return types that are derived from the same thing just cast the return type to the one of the base class? In my example, it seems like it would make sense to have (A*(new B))->blah() return a Inner that is really an Inner2 that has been cast up.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(6

深海里的那抹蓝2024-12-05 06:46:28

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

Only pointers and references can be covariant.

停顿的约定2024-12-05 06:46:28

想象一下你的例子有效。

考虑以下代码:

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

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

Imagine that your example worked.

Consider the following code:

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

If the dynamic type of a is B*, then a->blah() calls a::B->blah(), and returns a B::Inner. This is then silently sliced into an instance of A::Inner. In general, this (and any type of slicing) is not what you want. I find this to be a good restriction.

蓝天2024-12-05 06:46:28

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

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

If what you request would be allowed it would mean that a call to blah() via the base class must do a conversion from Inner2 to Inner... a cast is not possible since the caller is responsible for managing the returned object (since it is not returned by pointer/reference but by value) and will reserve space for it on the stack. So it can only handle Inner not Inner2 or whatever ancestor class.

So you then have an instance of Inner, not Inner2... So you don't get any advantage...

无尽的现实2024-12-05 06:46:28

这是为什么呢?

仅仅是因为 ISO C++ 标准委员会就是这么规定的!

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

Why is that?

Simply because the ISO C++ standard committee ruled it that way!

There is no fundamental reason. It could be done differently, at the cost of a tiny additional compiler complexity.

厌味2024-12-05 06:46:28

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

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

}

inr 必须同时是静态和动态类型“Inner”(而不是“Inner2”)才能从堆栈中分配 - 因此,如果 blah 返回一个 Inner2 ,它将用于构建内部,它的“2ness”将丢失(或如 K Dragon 提到的那样被切片)。

objects declared on the runtime stack must be known to the compiler at compile time:

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

}

inr must be of both static and dynamic type "Inner" (not "Inner2") to be allocated off the stack -- so if blah returned an Inner2 it would be used to construct an Inner and it's "2ness" would be lost (or sliced as K Dragon mentions).

只是在用心讲痛2024-12-05 06:46:28

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

The blah method have to return A::Inner in all cases -- if you think of the reason it is quite simple. A instance of B could easily be case to the base class A. Now if anybody calls blah on A (the base class which object should it return? Inner2 or Inner?

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文