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