代码之家  ›  专栏  ›  技术社区  ›  Brandon

重写虚拟函数协变返回类型(两个指针)

c++
  •  0
  • Brandon  · 技术社区  · 10 年前

    我正在尝试重写基类函数。派生类型和基类型都返回指针,因此根据我在谷歌和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 回复  |  直到 10 年前
        1
  •  5
  •   Brian Bi    10 年前

    MSVC是正确的; void* 不与共变 T* 引用标准(10.3[类.虚拟],第7节):

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

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

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

    两个指针或引用都具有相同的cv限定和返回类型中的类类型 D: :f 具有与返回类型中的类类型相同或更少的cv资格 B: :f .

    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 始终可以转换为 用户* .

    现在,它是 公平的 允许 U 成为 无效的 在这种情况下,因为任何指向对象类型的指针都可以转换为 无效* 尽管 无效的 不是任何东西的基类。但该标准并不要求允许这样做。

    该标准还表示(1.4【简介合规性】,第8段),

    符合性的实现可能具有扩展(包括附加的库函数),前提是它们具有扩展 不会改变任何格式良好的程序的行为。需要实施来诊断 使用根据本国际标准格式不正确的扩展。然而, 他们可以编译和执行这样的程序。

    所以g++不是 错误的 .允许 U 成为 无效的 是一个 扩大 它不会改变任何形式良好的程序的行为++ 尝试编译此代码时发出警告。

        2
  •  1
  •   David Rodríguez - dribeas    10 年前

    在这种情况下,gcc是错误的,VS拒绝代码是正确的。该标准在10.3/7中对此进行了处理,其中定义了 协变的 方法该定义要求两个返回类型都是指向 自从 void 不是类,您提供的代码不会显示 协方差 .

    Gcc错误地接受了代码。

        3
  •  0
  •   c-smile    10 年前

    MSVC2013是正确的。

    你什么时候打电话

      ptr->data();
    

    编译器不知道要使用什么方法 PSBaseObject::data() PSObject<data>::data();

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