覆盖虚函数协变返回类型(两个指针)

Posted

技术标签:

【中文标题】覆盖虚函数协变返回类型(两个指针)【英文标题】:overriding virtual function covariant return types (both pointers) 【发布时间】:2014-02-23 03:14:28 【问题描述】:

我正在尝试覆盖基类函数。派生类型和基类型都返回指针,因此根据我在 google 和 *** 上阅读的一些帖子,它应该是协变的。

但是在 MSVC2013 中为以下课程:

class PSBaseObject

    public:
        PSBaseObject() 
        virtual ~PSBaseObject() 

        virtual void* data()  return this; 
        virtual const void* data() const  return this; 
;

template<typename T>
class PSObject : public PSBaseObject

    private:
        T* data_;

    public:
        PSObject(T* object) : data_(object) 
        ~PSObject()  delete data_; 

        T* data()  return data_; 
        const T* data() const  return data_; 
;

我收到一个错误:

'PSObject<data>::data': overriding virtual function return type differs and is not covariant from 'PSBaseObject::data'

其中数据的定义和使用如下:

typedef struct

    void* hFileMap;
    void* pData;
    std::size_t size; 
 data;

data* info = new data();

auto ptr = new PSObject<data>(info);

为什么它不是协变的?

任何想法我在MSVC2013 做错了什么?代码在g++ 4.8.1 中编译并运行良好。

【问题讨论】:

void*T* 不协变——错误是否清楚? 如果是真的,为什么要用 g++ 编译和运行:ideone.com/TwTBRQ @CantChooseUsernames:因为 g++ 没有遵循标准。 【参考方案1】:

MSVC 是对的; void*T* 不协变。引用标准(10.3 [class.virtual],第 7 节):

覆盖函数的返回类型应与被覆盖函数的返回类型相同 函数或与函数的类协变。如果函数 D::f 覆盖函数 B::f,则 如果满足以下条件,则函数的返回类型是协变的:

——都是指向类的指针,都是指向类的左值引用,或者都是指向类的右值引用 类

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

— 指针或引用都具有相同的 cv 限定和 D::f 返回类型中的类类型 与B::f的返回类型中的类类型具有相同或更少的cv-qualification。

void 不是T (= data) 的基类,因此返回类型不是协变的。


那么为什么会有这条规则呢?好吧,这个想法是,如果你有

struct B 
    virtual U* f();
;

struct D : B 
    virtual V* f();
;

B* b1 = new B();
B* b2 = new D();
U* u1 = b1->f();
U* u2 = b2->f();

b1-&gt;f() 将调用B::f,它返回U*。但是b2-&gt;f() 将调用D::f,它返回V*V 必须从 U 派生,以便从 D::f 返回的 V* 始终可以转换为 U*

现在,在这种情况下允许Uvoid合理,因为任何指向对象类型的指针都可以转换为void*,即使void 不是任何东西的基类。但标准并没有要求允许。

标准还说(1.4 [intro.compliance],第 8 段),

一个符合标准的实现可以有扩展(包括额外的库函数),只要他们这样做 不改变任何格式良好的程序的行为。诊断程序需要实现 使用根据本国际标准格式错误的扩展。然而,这样做之后, 他们可以编译和执行这样的程序。

所以 g++ 没有。允许U 成为void 是一个扩展,它不会改变任何格式良好的程序的行为,并且当您尝试编译时,g++ 确实会发出警告这段代码。

【讨论】:

+1 提到 gcc 并非完全错误,因为这可以被视为扩展。【参考方案2】:

在这种情况下 gcc 是错误的,而 VS 在拒绝代码方面是正确的。该标准在 10.3/7 中对此进行了处理,其中定义了 covariant 的含义。该定义要求两种返回类型都是指向的指针或引用。由于void 不是类,因此您提供的代码不会显示协方差

Gcc 错误地接受了代码。

【讨论】:

【参考方案3】:

MSVC2013 是对的。

什么时候打电话

  ptr->data();

编译器不知道使用什么方法,可能是PSBaseObject::data()PSObject&lt;data&gt;::data();

所以你需要修正你的设计。

【讨论】:

但它确实知道(g++ 知道吗?)。该功能是虚拟的。它将调用子类的数据函数。如果它不存在,它会调用基类的:ideone.com/TwTBRQ 那么这里是哪个编译器?

以上是关于覆盖虚函数协变返回类型(两个指针)的主要内容,如果未能解决你的问题,请参考以下文章

C++笔记之多态

C++笔记之多态

C++笔记之多态

C++从入门到入土第十八篇:多态

C++从入门到入土第十八篇:多态

理解多态的语法使用以及多态的实现原理