9

s->duplicate()返回一个类型的对象Box*,但我在初始化它时遇到错误Box*。看起来它正在被转换回Shape*. 如果将协变返回类型转换回基类指针,那么它有什么意义?:

struct Shape
{
    virtual Shape* duplicate()
    {
        return new Shape;
    }
};

struct Box : Shape
{
    virtual Box* duplicate()
    {
        return new Box;
    }
};

int main()
{
    Shape* s = new Box;
    Box*   b = s->duplicate();
}

错误:

main.cpp:22:12: error: cannot initialize a variable of type 'Box *' with an rvalue of type 'Shape *'
    Box*   b = s->duplicate();
           ^   ~~~~~~~~~~~~~~
1 error generated.
4

2 回答 2

11

尽管在运行时(通过虚拟调度)被调用,并且虽然Box::duplicate 确实Box::duplicate 覆盖Shape::duplicate协变),并且虽然返回 a ,Box::duplicate Box*您仍然会得到一个Shape*指针,因为您是duplicate()通过Shape*指针调用的,并且Shape*是 的返回类型Shape::duplicate(),并且编译器只看到你调用Shape::duplicate,而不是Box::duplicate.

C++ 不能动态选择类型,所以这是它所能做的最好的。您Box*正在自动转换Shape*Box::duplicate. 正如 Barry 所说,“它仍然必须在编译时进行编译,而在编译时我们所知道的只是它返回一个Shape*”。

然后,要使其Box*再次成为 a,您需要显式地转换它(使用static_castor dynamic_cast),因为不存在隐式向下转换。

[C++11: 10.3/7]: 覆盖函数的返回类型应与被覆盖函数的返回类型相同或与函数的类协变[..]

[C++11: 10.3/8]:如果 的返回类型与 的返回类型D::f不同,则返回类型中B::f的类类型在D::f声明时应是完整的,D::f或者应为类类型D当覆盖函数作为被覆盖函数的最终覆盖者调用时,其结果将转换为(静态选择的)被覆盖函数(5.2.2)返回的类型。[..]

在标准文本中,一个相关的例子如下。

于 2015-05-09T20:44:31.843 回答
5

关键不是这样做:

Box*   b = s->duplicate();

这显然行不通,因为Shape::duplicate()返回 a Shape*。相反,Box*如果您直接调用duplicate()a ,则要接受 a Box

Box* old = new Box;
Box* b = old->duplicate(); // OK! We know it's a Box
于 2015-01-25T03:28:31.220 回答