Проблемы при избежании нарезки объектов с использованием CRTP - PullRequest
0 голосов
/ 15 декабря 2018

Я бы хотел избежать нарезки объектов с помощью 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 (квадрат, круг и т. Д.) В дальнейшем.

Код прилагается ниже.

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 голосов
/ 22 декабря 2018

Хорошо, я отвечаю на свой вопрос ... После следующего исправления код работал так, как я ожидал.

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
...
...
}
0 голосов
/ 15 декабря 2018

Измените строку:

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

на эту:

return dynamic_cast<T&>(*this);

Причина, по которой компилятор пожаловался на это, заключается в том, что вы пытаетесь вернуть ShapeCopyable, когда компилятор ожидаетссылка на конкретную форму, такую ​​как круг или квадрат.

Не существует неявного преобразования ссылки на базовый класс в ссылку на производный класс, что требует приведения.

...