0

Let's say I am trying to implement some math vector class.

As vector interface will be used in multiple places: array based vector, matrices return columns and rows as vector interface objects and etc.

I would like to overload +,- operators for my vectors. Each operator should return new constructed object of some vector implementation class.

But as you know operator overloading should return a value or a reference. I can not return a value, as I need runtime polymorphism, so I am left with references. But to have a reference that does not die after the function call object should be created in the heap.

So how should I manage the situation?

P.S. I could create a shared_ptr and return a reference to containing value, but it does not look like a good practice.

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
  • Honestly I don't see the need for multiple kinds of vectors. A vector is a vector is a vector. This seems like a problem with your requirements, analysis or design. Commented Feb 11, 2019 at 16:37
  • I can have a matrix class which will contain methods: getRow, getColumn. Matrix rows and columns should be a vector proxy for a matrix data access. I do not need high performance computation, as I could do it in assembly if needed. The question was for: the "correct" way of expressing my idea to c++ code. Commented Feb 11, 2019 at 16:43
  • I don't understand your requirements, suppose you want a special class matrix_row_proxy<T> which extends your vector<T>, which is supposedly what you need to proxy matrix access. Which kind of new object should be returned by its operator+? Why would you even need more than one implementation of such operator? Commented Feb 11, 2019 at 17:11
  • I agree with the crowd that your model is flawed. One way out of the question as asked is to return a unique_ptr or shared_ptr to the return object you have allocated? Then the client code deals entirely in smart pointers. Commented Feb 12, 2019 at 15:04

2 Answers 2

1

I suggest a slight modification to your design for accommodating the polymorphic nature of the implementation.

  1. Don't make vector polymorphic.
  2. Use a Data class to contain the implementation specific details of vector.
  3. Make Data polymorphic.

That will allow you to return vectors by value or by reference, as appropriate to an interface.

Sign up to request clarification or add additional context in comments.

4 Comments

Are you saying I should implement polymorphic storage class for a vector? Something similar to strategy pattern?
@gangan, It's similar to the strategy pattern. It's different from the strategy pattern in that the strategy to use is determined algorithmically instead of being defined by the user at run time.
As my vectors must create new vectors and I might need to change the default vector data storage I need to create something like VectorDataStorageFactory class and pass it to a vector. Ok, that is fair. So you are saying that in c++ most of the complex problems should be solved by not making top classes polymorphic and using something like delegate/strategy pattern to implement polymorphism?
@gangan, I hesitate to make that a generic recommendation. I think it fits well with your requirement.
0

Polymorphism by subtype is not the answer to all problems. I understand what are you trying to do but I don't exactly understand why a polymorphic by template solution is not enough and you need to have virtual operators (which don't mix well at all with polymorphism by subtype).

You want to be able to define operations on mixed types of vectors so that you can compute results between real containers and proxy to containers.

This first of all should require that you have a basic final type that you need, a proxy to a matrix column is not a real container but rather a view of a container, so adding two of them should return a real container (eg. a container backed by an actual std::array?).

A similar design could be managed by something like

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

The trick here is that operator+ is a template method which accepts a generic container of ElementType elements. The code assumes that these kind of containers provide a begin and end methods which return an iterator (which is a smart choice in any case because it works well with STL).

With you can do things like:

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;

So you can add heterogeneous kinds of vectors without the need of any virtual method.

Of course these methods require to create a new resulting object in the methods. This is done by copying this into a new vector_of<ContainerType, ElementType>. Nothing prevents you from adding a third template argument like VectorFactory which takes care of this so that you could use vectors which are only wrappers also on LHS of such operators.

3 Comments

I am not a fan of template methods as they can not be virtual :/
what kind of comment is it? virtual is a feature, you use it when you need it. Why do you need them to be virtual? virtual operator overloads are a awful design choice because they are meant to be used on values in any case.
Check the expression problem then :)

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.