【问题标题】:C++ inheritance of methods but wrong return types (automatic casting? typeid(*this) ?)方法的 C++ 继承但返回类型错误(自动转换?typeid(*this) ?)
【发布时间】:2014-06-10 12:39:45
【问题描述】:

我有一个带有返回类对象的方法的 BaseClass,并且我有一个 DerivedClass。现在当我有一个 DerivedClass 对象并调用 BaseClass 中定义的方法时,返回值是 ob 类型 BaseClass,不幸的是不是 DerivedClass 类型。

class BaseClass {
public:
  typeof(*this) myMethod1() {return *this;} // nice if that would work
  BaseClass& myMethod2() {return *this;}
  BaseClass myMethod3() {return BaseClass();}
};
class DerivedClass : public BaseClass {};

DerivedClass tmp;
tmp.myMethod1();
tmp.myMethod2();
tmp.myMethod3();
// all three methods should return an object of type DerivedClass,
// but really they return an object of type BaseClass

所以我希望实现的是使用超类的方法,但使用派生类的返回类型(自动转换?)。 myMethod1() 是我唯一能想到的,但它不起作用。

我已经搜索过,但没有找到任何令人满意的东西。

【问题讨论】:

    标签: c++ inheritance casting return return-type


    【解决方案1】:

    您想使用 CRTP (http://en.wikipedia.org/wiki/Curiously_recurring_template_pattern) 模式:

    template <class Derived>
    class BaseClass {
    public:
      Derived *myMethod1() {return static_cast<Derived *>(this);}
      Derived& myMethod2() {return static_cast<Derived&>(*this);}
    };
    class DerivedClass : public BaseClass<DerivedClass> {};
    
    DerivedClass tmp;
    tmp.myMethod1();
    tmp.myMethod2();
    

    【讨论】:

    • 注意:你不能实例化 BaseClass,我不相信你可以拥有动态多态性。也只有 myMethod1 返回一个 DerivedClass。
    • 感谢您的建议。不幸的是,我的 BaseClass 已经是一个模板,而您的方法似乎不是最适合我的问题。
    【解决方案2】:

    只需使用协变返回类型规则,就可以了。

    重申一下:如果您通过指针或引用返回,您的覆盖可以显式地具有从基类中返回的类型派生的返回类型,并且它是有效的虚拟覆盖。

    10.3 虚函数§7

    重写函数的返回类型应与被重写函数的返回类型相同 函数或与函数的类协变。如果函数 D::f 覆盖了函数 B::f,则 如果满足以下条件,则函数的返回类型是协变的:
    — 都是指向类的指针,都是指向类的左值引用,或者都是指向类的右值引用 类112
    — B::f 的返回类型中的类与 D::f 的返回类型中的类相同,或者是 D::f的返回类型中类的明确且可访问的直接或间接基类
    — 指针或引用都具有相同的 cv 限定和 D::f 的返回类型中的类类型 与 B::f 的返回类型中的类类型具有相同或更少的 cv 限定。

    如果你真的不想让它成为虚拟的,你可以在派生类中定义函数并回调基类。

    如果您有多个派生类,您可以考虑使用 CRTP 来加快速度:

    template <class X>
    struct CRTP : BaseClass {
      X& myMethod1() {return static_cast<X&>(*this;)} // covariant maybe-virtual
      X& myMethod2() {return static_cast<X&>(*this;)} // covariant maybe-virtual
      X myMethod3() {return X();} // Not virtual
    };
    struct DerivedClass1 : CRTP<DerivedClass1> {};
    struct DerivedClass2 : CRTP<DerivedClass2> {};
    struct DerivedClass3 : CRTP<DerivedClass3> {};
    

    【讨论】:

      【解决方案3】:

      我不能评论你的答案,虽然这应该是评论而不是答案。但无论如何。 我首先要说我不太确定这一点,但我认为您使用“Base to Derived”构造函数的解决方案存在几个问题。

      首先,如果您的 DerivedClass 中有任何成员不是您的 BaseClass 的成员,那么当调用 DerivedClass(const BaseClass&amp; src) 时,这些成员将被初始化为其默认值。这通常可能不是问题,但是当您使用

      时,这可能会被隐式调用
      DerivedClass someObject = ++someDerivedClassObject;
      

      编译器可能会这样称呼它:

      DerivedClass someObject = DerivedClass(++((BaseClass)someDerivedObject));
      

      其次,由于这是创建一个新对象,如果您将 DerivedClass-object 通过引用或按地址传递给某个函数并期望产生副作用,您可能会得到意想不到的结果,因为 ++ 创建了一个新对象。 但是,如果您有后缀增量 (operator++(int)),这可能正是您正在寻找的行为。

      这个帖子中已经给出的答案是我自己推荐的。 不确定“您的基类已经是模板”是什么意思。

      附言。正如我所说,根据 Stack Overflow 的规则,这不是一个好的答案,但我只是创建了一个帐户,所以我无法评论其他人的答案。也许有人可以确认并移动这个?

      【讨论】:

      • 这样的内容太长了,无法放入评论中,至少在我看来,这增加了答案部分的价值。至少你不会从我这里得到旗帜 :) 欢迎来到 SO!
      【解决方案4】:

      感谢您的回答。也许我的问题不够清楚,但我可以通过添加另一个构造函数自己解决我的问题。

      澄清一下:我有带有成员方法 operator+() 的 BaseClass 和 DerivedClass(没有该成员方法)。现在当我调用 DerivedClass 的 operator+() 时,返回类型是 BaseClass。我可以简单地将 operator+() 添加到 DerivedClass,但是,添加接受超类型(此处为 BaseClass)参数的 Copy-Constructor 解决了问题,并且返回类型自动变为正确:

      DerivedClass(const BaseClass& src) : BaseClass(src) {}
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2014-05-27
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2017-03-26
        • 1970-01-01
        相关资源
        最近更新 更多