0

假设我正在尝试实现一些数学向量类。

由于向量接口将在多个地方使用:基于数组的向量,矩阵返回列和行作为向量接口对象等。

我想为我的向量重载 +,- 运算符。每个运算符都应返回某个向量实现类的新构造对象。

但正如您所知,运算符重载应该返回一个值或一个引用。我无法返回值,因为我需要运行时多态性,所以我留下了引用。但是要有一个在函数调用对象之后不会死掉的引用应该在堆中创建。

那么我应该如何处理这种情况呢?

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;
    }
};
4

2 回答 2

1

我建议对您的设计稍作修改,以适应实现的多态性。

  1. 不要做vector多态。
  2. 使用一个Data类来包含vector.
  3. 使Data多态。

vector这将允许您根据接口的需要按值或按引用返回s。

于 2019-02-11T17:03:48.057 回答
0

亚型多态性并不是所有问题的答案。我了解您要做什么,但我不完全理解为什么通过模板解决方案的多态性是不够的,并且您需要使用虚拟运算符(它们与子类型的多态性完全不能很好地混合)。

您希望能够在混合类型的向量上定义操作,以便您可以计算真实容器和容器代理之间的结果。

这首先应该要求您具有所需的基本最终类型,矩阵列的代理不是真正的容器,而是容器的视图,因此添加其中两个应该返回一个真正的容器(例如由实际支持的容器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元素容器。代码假定这些容器提供了一个beginend返回迭代器的方法(这在任何情况下都是一个明智的选择,因为它与 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 上使用仅包装器的向量。

于 2019-02-11T17:41:52.470 回答