避免使用CRTP进行对象切片的问题

时间:2018-12-14 21:19:29

标签: templates crtp object-slicing

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

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

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

顺便说一句,Shape *的向量是所有Shape指针的持有者,它所保持的指针将在以后分配给它们对应的Shape(正方形,圆形等)向量。

代码附在下面。

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!
}

2 个答案:

答案 0 :(得分:0)

更改行:

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

对此:

return dynamic_cast<T&>(*this);

编译器抱怨的原因是,当编译器期望引用诸如Circle或Square之类的具体形状时,您试图返回ShapeCopyable。

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

答案 1 :(得分:0)

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

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
...
...
}