Использование полиморфизма с оператором + перегруженный шаблонный класс.Как реализовать базовый класс? - PullRequest
1 голос
/ 21 июня 2019

Я пытаюсь создать шаблонные классы для матриц и векторов разных размеров.Для моего класса Vector я перегружал операторы + = и +, чтобы иметь возможность добавлять два вектора одинаковой длины.Если длины не совпадают, я хочу, чтобы компилятор выдавал ошибку.Я хочу хранить несколько таких объектов mvc :: Vector (с разными длинами) внутри std :: vector.Для этого я создал базовый класс mvc :: VectorBase, от которого я наследую все объекты mvc :: Vector.Теперь я могу написать

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

Чтобы иметь возможность вызывать функции-члены MyVector из vectorBuffer, я добавил чисто виртуальные функции для этих членов, чтобы я мог использовать

vectorBuffer[0]->GetLength().

Моя проблема: яне может написать код, например, потому что VectorBase не знает перегрузки оператора.

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

Попытка добавить перегрузку оператора как чисто виртуальную в mvc :: VectorBase не сработала, потому что один аргумент должен быть аргументом шаблонаиз класса шаблонов mvc :: Vector, который я не могу использовать вне шаблона.

#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
}

Как реализовать желаемое поведение: vectorBuffer [0] + vectorBuffer [1] работает ТОЛЬКО, если объекты MyVectorсгенерированный с тем же шаблоном (длина равна)

Это уже работает с двумя отдельно сохраненными экземплярами MyVector.Сбой, когда я использую полиморфизм для хранения нескольких объектов mvc :: Vector в одном и том же std :: vector.

EDIT: Перегрузив оператор + с базовым классом в качестве возвращаемого типа, я получилзапрашиваемое поведение: оператор

#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 дважды перегружен, что также поддерживает «неполиморфное» использование.(см. пример внутри main)

Внутри оператора + переопределение является комментарием, использующим решение @Vikas Awadhiya dynamic_cast.Это тоже работает.В настоящее время я не знаю о производительности по сравнению с моим текущим решением с функцией виртуального геттера GetElem.

Пока мне удалось заставить его работать только с необработанными указателями.Все еще работаем над решением unique_ptr.

Спасибо за все ответы!

Ответы [ 2 ]

0 голосов
/ 21 июня 2019

Вы можете сделать это, вернув свой виртуальный operator+ и принять базовый класс:

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 голосов
/ 21 июня 2019

Я сделал несколько изменений в коде.

#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;

}

вывод: result.length = 2

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...