Question

I have this class to factorize common operations on N-dimensional vector spaces:

template <unsigned int N>
struct BaseVector
{
    float m_data[N];
    // Common operations like dot product, magnitude, test for unity, etc.
};

Note: I really want to factorize as much code as possible to minimize the amount of documentation and testing.

Now, I derive two classes:

// 3D geometric vectors
struct Vector3 : public BaseVector<3>
{
    Vector3 Cross(const Vector3& other);
    // ...
};

// Quaternions are a particular kind of vector space
struct Quaternion : public BaseVector<4>
{
    void Interpolate(const Quaternion& start, const Quaternion& end, ...);
    // ...
};

These classes behave similarly for addition and scalar multiplication (component-wise operation); so, I want to factorize operator+=() and operator*=() in the base vector class.

My question is: How do I return a reference of the proper type?

template <unsigned int N>
struct BaseVector
{
    ??? & operator+=(const BaseVector& other)
    {
        transform(m_data, m_data+N, other.m_data, m_data, plus<float>());
        return ???
    }
};

All my ideas so far (listed bellow) are not satisfying and I would appreciate some suggestions, thanks!


Idea #1: Use the C++ covariant return type mechanism. But then, I must overload these operators in the derived classes--am I right? (Which means duplicate testing to me.)

Idea #2: Go for templates?

template <unsigned int N>
struct BaseVector
{
    template <typename T2>
    T2 & operator+=(const T2& other)
    {
        transform(...);
        return *this; // THIS IS WRONG! I'm trying to "upcast"
    }
};

Idea #3: Factorize code into a private member in the base vector, but then I have to add more functions in derived classes (and more stuff to test)

template <unsigned int N>
struct BaseVector
{
private:
    void DoOperatorPlus(const BaseVector& other) { transform(...); }
};

struct Vector4 : public BaseVector<4>
{
    Vector4& operator+=(const Vector4& other)
    {
        DoOperatorPlus(other);
        return *this;
    }
};
Was it helpful?

Solution

You could actually try to use CRTP

The idea is that you give a template parameter to your base class of your derived class:

    template <unsigned int N, typename Derived>
    struct BaseVector
    {
        Derived & operator+=(const Derived& other)
        {
            transform(m_data, m_data+N, other.m_data, m_data, plus<float>());
            return static_cast<Derived&>(*this);
        }
    };

I am not 100% sure about the return statement but this should give you an idea.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top