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

时间:2014-02-23 03:14:29

标签: c++

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

但是在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中编译并正常工作。

3 个答案:

答案 0 :(得分:5)

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

  

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

     

- 两者都是指向类的指针,都是对类的左值引用,或者两者都是对它们的右值引用   类

     

- 返回类型B::f中的类与返回类型D::f中的类相同,或者是   返回类型D::f

中类的明确且可访问的直接或间接基类      

- 指针或引用在D::f的返回类型中具有相同的cv-qualification和类类型   与B::f的返回类型中的类类型具有相同的cv资格或更少的cv资格。

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,以便从V*返回的D::f始终可以转换为U*

现在,在这种情况下允许Uvoid 合理,因为任何指向对象类型的指针都可以转换为void*,甚至虽然void不是任何基类。但该标准并不要求允许它。

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

  

符合要求的实现可能具有扩展(包括其他库函数),只要它们可以   不改变任何格式良好的程序的行为。诊断程序需要实现   根据本国际标准使用不正确的扩展。但是,这样做了   他们可以编译和执行这样的程序。

所以g ++不是错误的。允许Uvoid是一个扩展名,它不会改变任何格式正确的程序的行为,并且g ++ 发出警告你试着编译这段代码。

答案 1 :(得分:1)

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

Gcc错误地接受了这段代码。

答案 2 :(得分:0)

MSVC2013是对的。

何时致电

  ptr->data();

编译器不知道使用哪种方法,可能是PSBaseObject::data()PSObject<data>::data();

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