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

2 Answers 2

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.

Sign up to request clarification or add additional context in comments.

2 Comments

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

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.