WhitAngl WhitAngl - 1 month ago 17
C++ Question

[C++]Covariant return types

I have a

VectorN
class, and a
Vector3
class inherited from
VectorN
(which can handle cross products for example). I have trouble determining the return types of the different operators. Example:

class VectorN
{
public:
VectorN(){};
virtual VectorN operator*(const double& d) {.....};
std::vector<double> coords;
};

class Vector3 : public VectorN
{
public:
Vector3(){};
virtual Vector3 operator*(const double& d) {....};
};


This particular example produces a C2555 error:


'Vector3::operator *': overriding virtual function return type differs and is not covariant from 'VectorN::operator *', see declaration of 'VectorN::operator *'.


The problem is that I don't return a reference to a
Vector3
, and that the
Vector3
class is not fully defined at the declaration of the
operator*
. However, I want my
operator*
to be virtual, and I want to return a
Vector3
when I multiply a
Vector3
with a constant (otherwise, if I do
(Vector3*double).crossProduct(Vector3)
, it would return an error).

What can I do ?

Thanks!

Answer

You need a re-design. First, prefer free-functions over member-functions. The only member functions you should have are the ones that need access to privates.

Start with this combo:

class VectorN
{
public:
   virtual VectorN& operator*=(double d)
    {
        /* ... */

        return *this;
    };
};


class Vector3 : public VectorN
{
public:
    virtual Vector3& operator*=(double d)
    {
        return static_cast<Vector3&>(VectorN::operator*=(d));
    };
};

Here covariance works fine because the type is a reference or pointer, and you re-use code. (static_cast is free, performance-wise, and safe since you know the derived type.)

Then you implement your free-functions:

// optimization: if you're going to make a copy, do it in the parameter list;
// compilers can elide a copy when working with temporaries
VectorN operator*(VectorN v, double d)
{
    // reuse code
    return v *= d;
}

VectorN operator*(double d, VectorN v)
{
    // reuse code
    return v *= d;
}

Do the same with Vector3.

What's been done is you get an easy way to write these operators because you get to use the core of the operator, and the return type matches, thanks to covariance.


Do heed warnings though, you probably don't need any of it. And extensions you want to make can be made via free-functions operating on a vector or valarray.

Comments