协变返回类型和类型转换

Covariant return type and type conversion

本文关键字:类型转换 返回类型      更新时间:2023-10-16

> 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.

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

C++ 无法动态选择类型,因此这是它能做的最好的事情。您的Box*在退出Box::duplicate的过程中自动转换为Shape*。正如 Barry 所说,"它仍然必须在编译时编译,在编译时,我们所知道的只是它返回一个Shape*"。

然后,要再次将其转换为Box*,您需要显式转换它(使用 static_castdynamic_cast ),因为不存在隐式下转换。

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

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

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

重点不是这样做:

Box*   b = s->duplicate();

这显然是行不通的,因为Shape::duplicate()返回一个Shape*.相反,关键是,如果您直接在Box上呼叫duplicate(),请接受Box*

Box* old = new Box;
Box* b = old->duplicate(); // OK! We know it's a Box