Follow

Keep Up to Date with the Most Important News

By pressing the Subscribe button, you confirm that you have read and are agreeing to our Privacy Policy and Terms of Use
Contact

How to design an override-safe factory method that allows subclasses to return covariant types?

I have a parent class that is designed to be const-safe, so I want to create "factory" methods that will return an altered version of itself in a newly constructed object. I want to be able to return by value and avoid heap allocation, similar in function to std::string::substr. This works fine in the parent class, but how to I allow a subclass to return override this method to return a covariant type, preferably without pointers or references?

I have a Point class that is provided to me that represents a 2D position:

class Point
{
    double x, y;

public:
    Point() : Point(0, 0)
    { }

    Point(double xpos, double ypos) : x{xpos}, y{ypos}
    { }

    double get_x() const { return x; }
    double get_y() const { return y; }

    virtual Point flip_x() const
    {
        return Point{x, -y};
    }

    virtual Point flip_y() const
    {
        return Point{-x, y};
    }
    ...
};

Notice that flip_x and flip_y are factory methods that return a new flipped Point object by value. This allows me to do something like const Point flipped = p.flip_x() without modifying the contents of const Point p.

MEDevel.com: Open-source for Healthcare and Education

Collecting and validating open-source software for healthcare, education, enterprise, development, medical imaging, medical records, and digital pathology.

Visit Medevel

I am implementing a subclass to Point called Point3D that supports an additional z depth coordinate. I want to be able to override these flip functions to return a Point3D instead, in order to be consistent with the return type of flip_z:

class Point3D : public Point
{
    double z;

public:
    Point3D() : Point3D(0, 0, 0)
    { }

    Point3D(double xpos, double ypos, double zpos)
    : Point(xpos, ypos), z{zpos}
    { }

    double get_z() const { return z; }

    // *** ERROR: return type is not covariant ***
    Point3D flip_x() const override
    {
        return Point3D{get_x(), -get_y(), -z};
    }

    // *** ERROR: return type is not covariant ***
    Point3D flip_y() const override
    {
        return Point3D{-get_x(), get_y(), -z};
    }
    
    Point3D flip_z() const
    {
        return Point3D{-get_x(), -get_y(), z};
    }
    ...
};

So these errors tell me that the return types are not covariant with the parent methods. As far as I can tell, my only options for using covariant returns are to use references or pointers. I cannot return a reference or pointer to a local variable, and using new to return a heap allocated Point adds unnecessary complexity to memory management. How can I achieve the effect of returning by value/static binding when returning a covariant type, so that the following is still valid:

const Point p{2, 2};
const Point flipped = p.flip_x();     // (2, -2)
const Point3D p3{2, 2, 2};
const Point3D flipped3 = p3.flip_x(); // (2, -2, -2)

Is this possible using this parent class and within these limitations, or will I have to switch to pointers & heap allocation?

>Solution :

If you remove virtual, you might do:

class Point
{
    double x, y;

public:
    Point() : Point(0, 0) { }
    Point(double xpos, double ypos) : x{xpos}, y{ypos} { }

    double get_x() const { return x; }
    double get_y() const { return y; }

    Point flip_x() const { return Point{x, -y}; }
    Point flip_y() const { return Point{-x, y}; }
    // ...
};

class Point3D : public Point
{
    double z;

public:
    Point3D() : Point3D(0, 0, 0) { }
    Point3D(double xpos, double ypos, double zpos) : Point(xpos, ypos), z{zpos}     {}

    double get_z() const { return z; }

    // hides parent ones
    Point3D flip_x() const { return Point3D{get_x(), -get_y(), -z}; }
    Point3D flip_y() const { return Point3D{-get_x(), get_y(), -z}; }
    
    Point3D flip_z() const { return Point3D{-get_x(), -get_y(), z}; }
    ///...
};
Add a comment

Leave a Reply

Keep Up to Date with the Most Important News

By pressing the Subscribe button, you confirm that you have read and are agreeing to our Privacy Policy and Terms of Use

Discover more from Dev solutions

Subscribe now to keep reading and get access to the full archive.

Continue reading