0

I'd like to avoid object slicing by using dynamic_cast. I'm trying to use CRTP to avoid writing assignment operator for every derived class. The base class is "Shape" and there are several derived classes("Circle" is an example). The purpose is to just use other Shape class as a template without writing assignment operator for each of them, like this class squre: public ShapeCopyable<square> However, the compiler complains at the line of return *this; saying:

error C2440: 'return': cannot convert from 'ShapeCopyable' to 'Circle &'

But it looks ok to me because the inheritance is this: Shape->ShapeCopable->Circle. I should be able to return an object of ShapeCopyable to a reference of Circle because they are from the same inheritance hierarchy, right? Where's the error? How should I fix it?

BTW, the vector of Shape* is the holder of all kinds of Shape pointers and the pointers held by it will be distributed to their corresponding Shape(square, circle, etc) vectors later on.

The code is enclosed below.

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!
}
Ben
  • 1
  • 1

2 Answers2

0

Change the line:

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

to this:

return dynamic_cast<T&>(*this);

The reason why the compiler complained about it is because you are trying to return a ShapeCopyable when the compiler is expecting a reference to a concrete shape like Circle or Square.

There is no implicit conversion from a reference to a base class to a reference to a derived class, which necessitates a cast.

MSS
  • 553
  • 2
  • 11
  • After I changed the `return`, it didn't complain on that but the code is still not working or ideal. 1. Ideally, I don't have to define `Circle & operator=(Shape const &rhs)` as in `class Circle` b/c totally avoiding `operator=` is the original purpose. But if I don't define it, The line `c3 = *shapes[0]` in main() complain: `no operator"=" matches this assignment: Circle=Shape`. Can we totally avoid `Circle& operator` since it's just a wrapper of `ShapeCopable` 2. The `assign(c)` called in Shapecopyable is the "assign" of `Shape`, not the one of `Circle', which is should be called. – Ben Dec 16 '18 at 18:14
  • To force it to call the `assign` of `Class Circle`, I changed the `assign(c)` to `T::assign(c)` in `class ShapeCopyable` but the compiler wouldn't take it and said `'Circle::assign': illegal call of non-static member function` – Ben Dec 16 '18 at 18:18
0

Ok, I'm answering my own question... After the following fix, the code worked as I expected.

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
...
...
}
Ben
  • 1
  • 1