Idiom of virtual constructor with smart pointers

I have a hierarchy of polymorphic classes, say, an abstract base class Shape along with its derived classes, for example. Rectangle , Circle , etc. Following the Virtual Designer Idiom , I was wondering why we need return types of functions of a virtual designer in derived classes, should it return the same type as in its parent class when using smart pointers?
For example, see the code below, where the clone() and create() member functions should return smart_pointers to the Shape class. However, when using simple pointers return types can be of the same type as the derived classes.
Can someone explain why we need to process these functions in the specified path?

 class Shape; typedef std::unique_ptr<Shape> shape_ptr; class Shape{ public: //typedef std::unique_ptr<Shape> shape_ptr; Shape(){}; virtual ~Shape(){}; virtual void draw() const = 0; virtual float area() const = 0; virtual shape_ptr clone() const = 0; virtual shape_ptr create() const = 0; //virtual Shape*clone() const = 0; //virtual Shape*create() const = 0; }; class Rectangle:public Shape{ public: typedef std::unique_ptr<Rectangle> rectangle_SmartPtr; Rectangle(int height=0, int width=0):m_Height(height),m_Width(width){}; Rectangle(const Rectangle & rect):m_Height(rect.m_Height),m_Width(rect.m_Width){}; ~Rectangle(){}; virtual void draw() const; virtual float area() const; //virtual rectangle_SmartPtr clone() const{ return rectangle_SmartPtr(new Rectangle(*this)); }; // error C2555: 'Rectangle::clone': overriding virtual function return type differs and is not covariant from 'Shape::clone' //virtual rectangle_SmartPtr create() const{ return rectangle_SmartPtr(new Rectangle()); }; // error C2555: 'Rectangle::create': overriding virtual function return type differs and is not covariant from 'Shape::create' virtual shape_ptr clone() const{ return shape_ptr(new Rectangle(*this)); }; //OK virtual shape_ptr create() const{ return shape_ptr(new Rectangle()); }; //OK //virtual Rectangle* clone() const{ return new Rectangle(*this); }; //OK //virtual Rectangle* create() const{ return new Rectangle(); }; //OK private: int m_Height; int m_Width; }; class Circle:public Shape{ public: typedef std::unique_ptr<Circle> circle_SmartPtr; Circle(float radius=0):m_Radius(radius){}; Circle(const Circle & other):m_Radius(other.m_Radius){}; ~Circle(){std::cout << "Circle destructor: " << this << std::endl; }; virtual void draw() const; virtual float area() const; //virtual circle_SmartPtr clone() const{ return circle_SmartPtr(new Circle(*this)); }; // error C2555: 'Circle::clone': overriding virtual function return type differs and is not covariant from 'Shape::clone' //virtual circle_SmartPtr create() const{ return circle_SmartPtr(new Circle()); }; // error C2555: 'Circle::create': overriding virtual function return type differs and is not covariant from 'Shape::create' virtual shape_ptr clone() const{ return shape_ptr(new Circle(*this)); }; //OK virtual shape_ptr create() const{ return shape_ptr(new Circle()); }; //OK //virtual Circle* clone() const{ return new Circle(*this); }; //OK //virtual Circle* create() const{ return new Circle(); }; //OK private: float m_Radius; }; 
+6
source share
2 answers

When using raw pointers, the compiler allows covariant return types, but this is not possible with smart pointers, because unique_ptr< Rectangle > cannot be obtained from unique_ptr< Shape > . These two classes are not completely related to the perspective of the compiler.

+6
source

This is called covariance.

In the class hierarchy, when the base class indicates a virtual method that returns either T* or T& , then derived classes are allowed to return U* or U& , respectively, provided that U comes from T (note: and, obviously, combinations of const and volatile )

This is a special rule checked by the compiler, and it works, because if U comes from T , then U* can be attributed to T* . Unfortunately, the rule is limited in that it does not work for any transformation, and therefore even if you can usually build unique_ptr<Shape> from unique_ptr<Rectangle> ... covariance does not work.

That's why, in its Cloneable concept, Boost mandates returns an empty pointer type. This is a shame, but the only way to get covariance.

+5
source

Source: https://habr.com/ru/post/906028/


All Articles