c++ 从基类方法返回对派生类的引用

aamkag61  于 2023-05-02  发布在  其他
关注(0)|答案(3)|浏览(114)

我的任务是实现一个简单的SVG生成器。我需要支持圆,折线和文本。这三种方法至少有四种:- SetStrokeColor - SetFillColor - SetStrokeWidth - ToString主要要求之一是支持链接,例如:例如:折线{}。SetStrokeColor(“白色”)。SetFillColor(“black”)。..
我决定实现一个基类Element,所有其他类都从它继承。这个想法是有一个类Document,它包含添加到文档中的所有元素的向量。基本方法的示例签名:

// source of trouble
Element &SetStrokeColor(const Color &color) {
    ...
    return *this;
}

我的派生类确实调用了这些方法,但问题是这些方法返回的是对基类Element的引用,而不是对派生类的引用。
我的问题是,所有这些是否都可以在C++中实现???
Further discussion here

bihw5rsg

bihw5rsg1#

如果你想共享实现 * 和 * 保存类型信息,CRTP就是你所需要的:

struct ElementBase { };

template <class Concrete>
struct Element : ElementBase {

    Concrete &setStrokeWidth(int width) {
        // Actual implementation...
        (void) width;

        return cthis();
    }

private:
    friend Concrete;
    Element() = default;

    Concrete &cthis() { return static_cast<Concrete &>(*this); }
    Concrete &cthis() const { return static_cast<Concrete const &>(*this); }
};

struct Circle : Element<Circle> {
    Circle &SetCircleCenter(int x, int y) {
        // Actual implementation...
        (void) x;
        (void) y;

        return *this;
    }
};

int main() {
    Circle c;
    c.setStrokeWidth(4).SetCircleCenter(0, 0);
}

See it live on Wandbox

pod7payv

pod7payv2#

使用协变返回类型,您可以

class Element {
  public:
    // ...

    virtual Element& refToThis() { return *this; };
};

在派生类中

class Derived : public Element {
  public:
    // ...

    Derived& refToThis() override { return *this; };
};

当静态类型为Derived时,它允许您将Derived示例作为Derived示例处理(例如,例如在Derived本身内部)。当静态类型是Element时,refToThis()的返回类型也是。

wn9m85ua

wn9m85ua3#

比较:

class Base {};
class Derived : public Base {};

Derived d;
Base* p = &d; // points to a Base that in reality is a derived
Base& b =  d; // in this respect, references do not differ...

// and you can get the original type back:
auto dr = static_cast<Derived&>(b);  // if you know 100% for sure that b is a Derived
auto dp = dynamic_cast<Derived*>(p); // if p might point to another type; you get
                                     // a null pointer back, if it does

返回this/*this的指针或引用绝对没有区别,所以是的,你可以安全地这样做。
编辑:
Circle().SetStrokeWidth(16).SetCircleCenter({0, 0}). SetStrokeWidth返回对Element的引用,因此SetCircleCenter不可用。
在这种情况下,你有点麻烦。就像lubgrdenoted一样,你可以通过使用协变返回类型来解决这个问题--是的,这意味着你必须单独覆盖每个函数。或者,您可以使用CRTP来保存所有这些麻烦:

template <typename T>
class SomeAppropriateName : public Element
{
public:
    T& setStrokeWidth(unsigned int width)
    {
        Element::setStrokeWidth(width);
        return static_cast<T&>(*this);
    }
};

class Circle : public SomeAppropriateName<Circle>
{
    // the overrides needed are inherited...
};

对于这种方法,来自Element类的原始函数需要是 * 非 * 虚拟的(感谢@Quentin的提示;这实际上是一个优点,因为虚拟函数调用的成本更高。..);在模板中,参数(T,i.例如Circle)尚未完全定义(使用CRTP模式),编译器无法检查返回类型是否真的是协变的。所以实际上,我们只是隐藏了基类的函数。
与覆盖相比,这仍然没有缺点:协变返回类型仅在(虚)函数直接在对象上调用时才可用:

Circle c;
c.setStrokeWidth(7).setCenter();

Element& e = c;
e.setStrokeWidth(7).setCenter(); // fails with both variants, i.e. even if you
                                 // implement co-variant override

相关问题