【问题标题】:The issues when avoiding object slicing using CRTP避免使用 CRTP 进行对象切片时的问题
【发布时间】:2018-12-14 21:19:28
【问题描述】:

我想通过使用 dynamic_cast 来避免对象切片。我正在尝试使用 CRTP 来避免为每个派生类编写赋值运算符。基类是“Shape”,并且有几个派生类(“Circle”就是一个例子)。目的是仅使用其他 Shape 类作为模板,而不为每个类编写赋值运算符,例如 class squre: public ShapeCopyable<square> 但是,编译器在 return *this; 的行抱怨说:

错误 C2440:“return”:无法从“ShapeCopyable”转换为“Circle &”

但它看起来不错,因为继承是这样的:Shape->ShapeCopable->Circle。我应该能够将ShapeCopyable 的对象返回到Circle 的引用,因为它们来自相同的继承层次结构,对吧?错误在哪里?我应该如何解决它?

顺便说一句,Shape* 的向量是各种 Shape 指针的持有者,它持有的指针稍后会分配到它们对应的 Shape(square, circle, etc) 向量中。

代码附在下面。

class Shape {

protected:
    string name;
    int edges;
    virtual void assign(const Shape &rhs) {
        name = rhs.name;
        edges = rhs.edges;
    }
};

template<typename T>
class ShapeCopyable : public Shape
{
public:
    T & operator=(const Shape& s)
    {
        T const& c = dynamic_cast<T const&>(s);  // Throws on bad cast.
        assign(c);
        return *this; //The compiler complains at this line
    }
};

class Circle: public ShapeCopyable<Circle> {
private:
    int radius;
public:
    // preferably, this operator= is not needed.  
    Circle & operator=(Shape const &rhs) {
        ShapeCopyable<Circle>::operator=(rhs);
        return *this;
    } 
    Circle(int in = 0) :radius(in) {}
    std::string getName() { return name; }
    int getEdges() { return edges; }
    int getRadius() { return radius; }
    void setRadius(int r) { radius = r; }

protected:
    void assign(const Circle & rhs) {
        Shape::assign(rhs);
        radius = rhs.radius;
    }
};

main()
{
    std::vector<Shape*> shapes;
    std::vector<Circle*> circs;
    Circle c2(5); //Creates a circle with 5 for the radius.
    shapes.push_back(&c2); //Pushing the 5-radius circle into the Shapes* vector
    Circle c3; //Creates a circle with default constructor (which does NOT define radius)
    c3 = *shapes[0]; //Now, the overloaded assignment operator. Look at Circle::assign(const Shape&) function
    circs.push_back(&c3); //We push our newly assigned circle to our Circle vector
    std::cout << "c3 radius: " << circs[0]->getRadius(); //This will be 5!
}

【问题讨论】:

    标签: templates crtp object-slicing


    【解决方案1】:

    换行:

    return *this; //The compiler complains at this line
    

    到这里:

    return dynamic_cast<T&>(*this);
    

    编译器抱怨它的原因是,当编译器期望引用圆形或方形等具体形状时,您正试图返回 ShapeCopyable。

    没有从对基类的引用到对派生类的引用的隐式转换,这需要强制转换。

    【讨论】:

    • 在我更改了return 之后,它并没有抱怨,但代码仍然无法正常工作或不理想。 1. 理想情况下,我不必像class Circle b/c 那样定义Circle &amp; operator=(Shape const &amp;rhs),完全避免operator= 是最初的目的。但如果我不定义它,main() 中的 c3 = *shapes[0] 行会抱怨:no operator"=" matches this assignment: Circle=Shape。我们可以完全避免Circle&amp; operator,因为它只是ShapeCopable 2 的包装。Shapecopyable 中调用的assign(c) 是Shape 的“赋值”,而不是应该调用的“Circle”。
    • 为了强制它调用Class Circle的assign,我将class ShapeCopyable中的assign(c)更改为T::assign(c),但编译器不接受它并说'Circle::assign': illegal call of non-static member function
    【解决方案2】:

    好的,我正在回答我自己的问题...经过以下修复后,代码按预期工作。

    template<typename T>
    class ShapeCopyable : public Shape
    {
    public:
        T & operator=(const Shape& s)
        {
            T const& c = dynamic_cast<T const&>(s);  // Throws on bad cast.
            static_cast<T*>(this)->assign(c); //this line got fixed
            return dynamic_cast<T&>(*this); //this line got fixed
        }
    };
    
    class Circle: public ShapeCopyable<Circle> {
    private:
        int radius;
    public:
        using Shapecopyable<Circle>::operator=; //this line got fixed
    
        Circle(int in = 0) :radius(in) {}
        std::string getName() { return name; }
        int getEdges() { return edges; }
        int getRadius() { return radius; }
        void setRadius(int r) { radius = r; }
    //The rest code is the same as before
    ...
    ...
    }
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2019-03-28
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多