Полиморфизм времени выполнения c ++ 11 и перегрузка операторов - PullRequest
0 голосов
/ 11 февраля 2019

Допустим, я пытаюсь реализовать некоторый математический векторный класс.

Поскольку векторный интерфейс будет использоваться в нескольких местах: вектор на основе массива, матрицы возвращают столбцы и строки в качестве объектов векторного интерфейса и т. Д.

Я хотел бы перегрузить операторы +, - для своих векторов,Каждый оператор должен возвращать новый построенный объект некоторого класса векторной реализации.

Но, как вы знаете, перегрузка оператора должна возвращать значение или ссылку.Я не могу вернуть значение, так как мне нужен полиморфизм времени выполнения, поэтому у меня остались ссылки.Но чтобы иметь ссылку, которая не умирает после того, как объект вызова функции должен быть создан в куче.

Так как мне управлять ситуацией?

PS Я мог бы создать shared_ptr и вернуть ссылку на содержащее значение, но это не выглядит хорошей практикой.

typedef unsigned int vector_idx_t;

template <class T, vector_idx_t size>
class vector {
public:
    virtual ~vector();

    virtual T& operator[](const vector_idx_t idx) = 0;
    virtual vector<T, size>& operator+ (const T& a) const = 0;
    virtual vector<T, size>& operator- (const T& a) const = 0;
    virtual vector<T, size>& operator* (const T& a) const = 0;
    virtual vector<T, size>& operator/ (const T& a) const = 0;

    virtual vector<T, size>& operator+ (const vector<T, size>& vec2) const = 0;
    virtual vector<T, size>& operator- (const vector<T, size>& vec2) const = 0;
};

template <class T, vector_idx_t size>
class array_vector: public vector<T, size> {
private:
    std::array<T, size> m_elements;
public:
    array_vector();
    array_vector(std::array<T, size> elements);
    array_vector(const vector<T, size>& vec2);
    array_vector(std::initializer_list<T> elems);

    virtual ~array_vector();

    virtual T& operator[](const vector_idx_t idx) {
           return m_elements[idx];
        }

    virtual vector<T, size>& operator+ (const T& a) const {
        std::array<T, size> e;
        for (vector_idx_t i = 0; i < size; ++i) {
            e[i] = m_elements[i] + a;
        }
        auto v = std::make_shared<array_vector<T, size>>(elems);
        return *v;
    }
};

Ответы [ 2 ]

0 голосов
/ 11 февраля 2019

Полиморфизм по подтипам не является решением всех проблем.Я понимаю, что вы пытаетесь сделать, но я не совсем понимаю, почему решения по полиморфному шаблону недостаточно, и вам нужно иметь виртуальные операторы (которые вообще плохо сочетаются с полиморфизмом по подтипу).

Вы хотите иметь возможность определять операции над смешанными типами векторов, чтобы можно было вычислять результаты между реальными контейнерами и прокси-серверами для контейнеров.

Для этого прежде всего необходимо иметь базовый конечный тип, который вынужно, чтобы прокси для столбца матрицы был не реальным контейнером, а скорее видом контейнера, поэтому добавление двух из них должно вернуть реальный контейнер (например, контейнер, поддерживаемый фактическим std::array?).

Подобным дизайном может управлять что-то вроде

template<typename ContainerType, typename ElementType>
class vector_of : public ContainerType
{
public:
  vector_of(const ContainerType& container) : ContainerType(container) { }

  vector_of<ContainerType, ElementType> operator+(const ElementType& a) const
  {
    vector_of<ContainerType, ElementType> copy = vector_of<ContainerType,ElementType>(*this);
    std::for_each(copy.begin(), copy.end(), [&a](ElementType& element) { element += a; });
  }

  template<typename T>
  vector_of<ContainerType, ElementType> operator+(const vector_of<T, ElementType>& a) const
  {
    vector_of<ContainerType, ElementType> copy(*this);
    auto it = copy.begin();
    auto it2 = a.begin();

    while (it != copy.end() && it2 != a.end())
    {
      *it += *it2;

      ++it;
      ++it2;
    }

    return copy;
  }
};

Хитрость здесь в том, что operator + - это метод шаблона, который принимает общий контейнер из элементов ElementType.Код предполагает, что такого рода контейнеры предоставляют методы begin и end, которые возвращают итератор (который в любом случае является разумным выбором, поскольку он хорошо работает с STL).

С помощью вы можете делать вещинапример:

class MatrixRowProxy
{
private:
  int* data;
  size_t length;

public:
  MatrixRowProxy(int* data, size_t length) : data(data), length(length) { }

  int* begin() const { return data; }
  int* end() const { return data + length; }
};

vector_of<std::array<int, 5>, int> base = vector_of<std::array<int, 5>, int>({ 1, 2, 3, 4, 5 });
vector_of<std::vector<int>, int> element = vector_of<std::vector<int>, int>({ 2, 3, 4, 5, 6 });

int* data = new int[5] { 10, 20, 30, 40, 50};
vector_of<MatrixRowProxy, int> proxy = vector_of<MatrixRowProxy, int>(MatrixRowProxy(data, 5));

auto result = base + element + proxy;
for (const auto& t : result)
  std::cout << t << std::endl;

Таким образом, вы можете добавлять гетерогенные виды векторов без использования какого-либо метода virtual.

Конечно, эти методы требуют создания нового результирующего объекта в методах.Это делается путем копирования этого в новый vector_of<ContainerType, ElementType>.Ничто не мешает вам добавить третий аргумент шаблона, такой как VectorFactory, который позаботится об этом, чтобы вы могли использовать векторы, которые являются только обертками, также в LHS таких операторов.

0 голосов
/ 11 февраля 2019

Я предлагаю небольшую модификацию вашего дизайна для учета полиморфного характера реализации.

  1. Не делайте vector полиморфным.
  2. Используйте класс Dataсодержать специфические для реализации детали vector.
  3. Make Data polymorphic.

, что позволит вам возвратить vector s по значению или по ссылке, в зависимости от ситуациик интерфейсу.

...