覆盖虚函数协变返回类型(两个指针)
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->f()
将调用B::f
,它返回U*
。但是b2->f()
将调用D::f
,它返回V*
。 V
必须从 U
派生,以便从 D::f
返回的 V*
始终可以转换为 U*
。
现在,在这种情况下允许U
为void
是合理,因为任何指向对象类型的指针都可以转换为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<data>::data();
所以你需要修正你的设计。
【讨论】:
但它确实知道(g++ 知道吗?)。该功能是虚拟的。它将调用子类的数据函数。如果它不存在,它会调用基类的:ideone.com/TwTBRQ 那么这里是哪个编译器?以上是关于覆盖虚函数协变返回类型(两个指针)的主要内容,如果未能解决你的问题,请参考以下文章