1
votes

I am trying to create template classes for matrices and vectors of different sizes. For my Vector class I overloaded the += and + operator to be able to add two vectors of same length. If lengths do not match I want the compiler to throw an error.

I want to store multiple of those mvc::Vector objects (with different lenghts) inside a std::vector.

To do that I created a base class mvc::VectorBase which I inherit all mvc::Vector objects from.

Now I can write

std::vector<std::unique_ptr<mvc::VectorBase>> vectorBuffer;

To be able to call member functions of MyVector from vectorBuffer I added pure virtual functions for those members so that I can use

vectorBuffer[0]->GetLength().

My problem: I can't write code like because VectorBase does not know the operator overload.

mvc::Vector<2> result = (*vectorBuffer[0]) + (*vectorBuffer[1]);

Trying to add operator overloading as pure virtual to mvc::VectorBase did not work because one argument would have to be a template argument from mvc::Vector template class, which I can´t use outside the template.

#include <vector>
#include <memory>

#define T float

namespace mvc
{
    class VectorBase
    {
    public:
        // virtual Vector operator+= (Vector<Tlength>& other) = 0;
        virtual int GetLength() const = 0;
    };

    template<int Tlength>
    class Vector : public VectorBase
    {
    private:
        T vec[Tlength];

    public:
        Vector operator+ (Vector<Tlength>& other)
        {
            for (int i = 0; i < Tlength; i++)
            {
                vec[i] += other.vec[i];
            }
            return *this;
        }

        int GetLength() const
        {
            return Tlength
        }
    }
}

int main()
{
    mvc::Vector<3> vec3_1;
    mvc::Vector<3> vec3_2;
    mvc::Vector<4> vec4_1;

    mvc::Vector<3> result = vec3_1 + vec3_2; // this line works properly 
    mvc::Vector<3> result = vec3_1 + vec4_1; //this line won´t compile (as expected)

    std::vector<std::unique_ptr<mvc::VectorBase>> vectorBuffer;

    vectorBuffer.push_back(std::make_unique<mvc::Vector<2>>());
    vectorBuffer.push_back(std::make_unique<mvc::Vector<2>>());

    mvc::Vector<2> result = (*vectorBuffer[0]) + (*vectorBuffer[1]); // <-- this is what i want to be able to do
}

How do I implement the desired behavior?

vectorBuffer[0] + vectorBuffer[1] works ONLY if MyVector objects are generated with the same template (Tlength is equal)

This already works with two separately stored instances of MyVector.

It fails when I use polymorphism to store multiple mvc::Vector objects in the same std::vector.

EDIT:

By overloading + operator with with base class as return type I got the requested behavior:

#include <vector>
#include <memory>
#include <iostream>

#define T float

namespace mvc
{
    class VectorBase
    {
    public:
        virtual VectorBase* operator+ (VectorBase& other) = 0;
        virtual int GetLength() const = 0;
        virtual T GetElem(int i) const = 0;
        virtual void Print() const = 0;
    };

    template<int Tlength>
    class Vector : public VectorBase
    {
    private:
        T vec[Tlength];

    public:
        Vector(T initValue)
        {
            for (int i = 0; i < Tlength; i++)
            {
                vec[i] = initValue;
            }
        }

        VectorBase* operator+ (VectorBase& other) override
        {
            if (other.GetLength() != Tlength)
            {
                std::cout << "[Error]: Argument dimensions mismatch. Program will terminate." << std::endl;
                std::cin.get();
                exit(-1);
            }

            //Vector<Tlength> tmpOther = dynamic_cast<Vector<Tlength>&>(other);

            for (int i = 0; i < Tlength; i++)
            {
                //vec[i] += tmpOther.vec[i];
                vec[i] += other.GetElem(i);
            }
            return this;
        }


        Vector<Tlength> operator+ (Vector<Tlength>& other)
        {
            for (int i = 0; i < Tlength; i++)
            {
                vec[i] += other.GetElem(i);
            }
            return *this;
        }

        int GetLength() const override
        {
            return Tlength;
        }

        T GetElem(int i) const override
        {
            return vec[i];
        }

        void Print() const override
        {
            for (int i = 0; i < Tlength; i++)
            {
                std::cout << " " << vec[i] << "\n";
            }
            std::cout << std::endl;
        }
    };
}

int main()
{
    /* without polymorphism */
    // vector1
    mvc::Vector<2> vec3_1 = mvc::Vector<2>(1.2f);
    vec3_1.Print();
    // vector2
    mvc::Vector<2> vec3_2 = mvc::Vector<2>(3.4f);
    vec3_2.Print();
    // vector2 = vector1 + vector2
    vec3_2 = vec3_1 + vec3_2;
    vec3_2.Print();

    /* with polymorphism */
    // vector buffer storing base class objects
    std::vector<mvc::VectorBase*> vectorBuffer;
    //vector1
    vectorBuffer.push_back(new mvc::Vector<3>(3.5f));
    vectorBuffer[0]->Print();
    //vector2
    vectorBuffer.push_back(new mvc::Vector<3>(2.8f));
    vectorBuffer[1]->Print();
    //vector2 = vector1 + vector2
    vectorBuffer[1] = *vectorBuffer[0] + *vectorBuffer[1];
    vectorBuffer[1]->Print();

    std::cin.get();

    for (unsigned int i = 0; i < vectorBuffer.size(); i++)
    {
        delete vectorBuffer[i];
    }
}

plus operator is overloaded twice to also support "non polymorphic" usage. (see example inside main)

Inside the operator+ override is a commend using @Vikas Awadhiya ´s dynamic_cast solution. This also works. Currently I do not know about performance compared to my current solution with virtual getter function GetElem.

For now I was only able to get it working with raw pointers. Still working on a unique_ptr solution.

Thanks for all replys!

2
First you should know that at the last line you're trying to add pointers. Dereference them first. Second, before you start adding Vectors you don't know the size of, consider if that is really what you want. What should the return type be ? And what should happen if the size doesn't match at runtime ? The answer you're looking for will depend on those questions. - Arne J
It doesn't work this way. Either you (and the compiler) know the length at compile time, because it's encoded in the type, or you (and the compiler) don't. You need to decide one way or the other. - n. 1.8e9-where's-my-share m.
@ArneJ By using auto I didn´t really think about the return type (which also produced that dereferencing mistake). I fixed that in my original post. If the two types do not match at runtime I would want the program to terminate with an error message. - sphexoo
@n.m. ok, but which way does it work? Are you saying that there is no way to implement a templated class which inherits from a base class to store its childs inside one container and then use operator+ overloading to add objects from the exact same templated class but fail when trying to add two child objects which are generated from different templates? - sphexoo
You are storing pointers to base clsss. The derived type is not known at compile. No, you cannot both know and not know the types at the same time. - n. 1.8e9-where's-my-share m.

2 Answers

0
votes

You can do this by making your virtual operator+ return and accept the base class:

class VectorBase
{
public:
    virtual int GetLength() const = 0;
    // We have to return a heap allocated object because the actual type and,
    // hence, its size is unknown
    virtual std::unique_ptr<VectorBase> operator+(VectorBase& other) = 0;
};

template<int Tlength>
class Vector: public VectorBase
{
private:
    // ...

    std::unique_ptr<VectorBase> operator+(VectorBase& other) override
    {
        if (other.GetLength() != Tlength)
            return nullptr; // or throw an exception if you want

        Vector result = *this + static_cast<Vector<Tlength>&>(other);
        // or "new Vector<Tlength>(result)" if your compiler doesn't support C++14
        return std::make_unique<Vector<Tlength>>(result);
    }

    // ...
};
0
votes

I have made few changes in code see this,

#include <iostream>
#include <vector>
#include <memory>

namespace mvc
{

class VectorBase
{
    public:
    virtual ~VectorBase(){}

    virtual VectorBase& operator+ ( VectorBase& other) = 0;
    virtual int GetLength() const = 0;
};

template<int length>
class Vector: public VectorBase
{
private:
    double vec[length];

public:
    Vector(): VectorBase(),
        vec{}
    {

    }

    VectorBase& operator+ ( VectorBase& other) override
    {
        Vector< length>& subOther = dynamic_cast< Vector< length>&>( other);

        for ( int i = 0; i < length; i++)
        {
            vec[i] += subOther.vec[ i];
        }
        return *this;
    }

    int GetLength() const
    {
        return length;
    }


};

}


int main()
{
    std::vector<std::unique_ptr<mvc::VectorBase>> vectorBuffer;

    vectorBuffer.push_back( std::make_unique<mvc::Vector< 2>>());
    vectorBuffer.push_back( std::make_unique<mvc::Vector< 2>>());

    mvc::Vector< 2> result = dynamic_cast< mvc::Vector< 2>&>( *vectorBuffer[ 0] + *vectorBuffer[ 1]);

    std::cout<< "result.length = "<< result.GetLength()<< std::endl;

}

output: result.length = 2