1

Problem statement: Let Matrix be a base class which is subclassed by DenseMatrix and SparseMatrix (and possibly others). What I would like to achieve is the following:

Matrix *A = new DenseMatrix();
Matrix *B = new SparseMatrix();
Matrix C = (*A) + (*B); // dense + sparse
Matrix D = (*A) + (*A); // dense + dense
Matrix E = (*B) + (*B); // sparse + sparse

Even better, I would like to have the following:

DenseMatrix C = (*A) + (*B);
DenseMatrix D = (*A) + (*A);
SparseMatrix E = (*B) + (*B);

Now, when adding a DenseMatrix with a SparseMatrix having declared both as Matrix implies that there must be an operator+ definition in Matrix.

I have already read this answer which makes use of an interface AddEnabled<Foo>, but doesn't seem to be a good solution when (almost) any possible combination of summands. I could possibly define in DenseMatrix the following functions:

friend DenseMatrix operator+ (DenseMatrix const& left, DenseMatrix const& right);

But then again it will be impossible to add two instances of DenseMatrix declared as Matrix (i.e., Matrix *A = new DenseMatrix();).

From various similar questions and answers I suspect that the pimpl idiom could be relevant, but I don't see how.

Note: I'm coding in C++98, not C++11.

Update: As Dieter Lücking suggested in his answer an opeator+ needs to be introduced in the base class. This makes sense, but the problem is that Matrix, being abstract, does not allow methods which return abstract types. However, it is possible to return a pointer or a reference to Matrix; this way we would have a definition like:

Matrix& operator+(const Matrix& right) const;

To an extent this would work, but users of my code would expect a + to return a Matrix instead of a reference to one.

Community
  • 1
  • 1
Pantelis Sopasakis
  • 1,902
  • 5
  • 26
  • 45

3 Answers3

1

You may give the base class a state indicating the matrix layout - having that, dispatch matrix operations (on the base class) accordingly. Keep the special matrices classes for construction, but they will elide to the base matrix after applying an operation.

Example:

Matrix = IdentityMatrix operation DiagonalMatrix

This would elide the argument types and result in a matrix having a state 'Diagonal'

  • So, you mean that I should implement methods like `DenseMatrix DenseMatrix::add(DenseMatrix&)` and `DenseMatrix DenseMatrix::add(SparseMatrix&)` and have a method like `int Matrix::getType();` so that I know what each object it, so that then I will be able to define a single `operator+` in the base class? – Pantelis Sopasakis Jul 09 '15 at 19:07
  • No, just Matrix operator Matrix (where the base class Matrix chooses the proper algorithm, depending on the states of the arguments) –  Jul 09 '15 at 19:13
  • True, but as I commented to @JohnP's answer, the definition of a `Matrix operator+(const GenericMatrix& right) const;` in `Matrix` results in an *"invalid abstract return type"* because `Matrix` is an abstract class, i.e., it has some other abstract methods. Maybe I could introduce some dummy implementations and throw an exception if one attempts to create instances of `Matrix`. – Pantelis Sopasakis Jul 09 '15 at 23:35
  • Also - given that `Matrix` contains abstract methods - does it make sense for the addition operator to return a pointer to `Matrix` instead of `Matrix`? – Pantelis Sopasakis Jul 09 '15 at 23:43
0

You might need to declare your Matrix::operator+ as virtual if you re-define it in your sub-classes and you initialize them as

Matrix *A = new DenseMatrix();

Also why is operator+ not a member of DenseMatrix? What I mean is

DenseMatrix DenseMatrix::operator+ (DenseMatrix const& right) const;

instead of

friend DenseMatrix operator+ (DenseMatrix const& left, DenseMatrix const& right);

Are you sure you need to re-overload operator+ in you derived classes again? Can't you just inherit operator+ from your base class Matrix?

Edit: If your base class does not have an operator+, but your derived ones do, you still need to declare (but not define) one as virtual in the base class, otherwise the derived classes cannot override it when they are pointed to by a Matrix* and not a DenseMatrix*.

John P
  • 1
  • 2
  • If I attempt to declare a virtual abstract summation operator in the base class, i.e., `virtual Base operator+(const Base& right) const =0;` then I get a **invalid abstract return type for member function**. If I declare an `operator+` in my DenseMartix then (i) I will be able to do only **DenseMatrix+DenseMatrix** and not **DenseMatrix+SparseMatrix** and (ii) I will not be able to do what I explained in the problem statement in my question. – Pantelis Sopasakis Jul 09 '15 at 19:05
  • Why would you not be able to do Dense+Sparse? Also, how is adding these matrices different from adding Matrix+Matrix? Why can't you simply define `Matrix::operator+(Matrix)`? What type would Sparse+Dense be? What type would Dense+Sparse be? – John P Jul 09 '15 at 19:38
  • If my base class `Matrix` is abstract then I get a "invalid abstract return type for member function ‘Matrix Matrix::operator+(const Matrix&) const’". Should my base class have at least a "fake" implementation for every method I declare? – Pantelis Sopasakis Jul 09 '15 at 23:30
0

Not sure why you want multiple + operators. Matrix addition is the same, no matter what the matrix representation:
1. Ensure the matrices being added have the same dimensions.
2. Add corresponding entries from the input matrices to produce the values in the output matrix.
These operations will be done in the base class operator +.

Then all you need is to implement getDimensions() in each subclass and, if they are equal, perform:
result.put(x, y, inputA.get(x, y) + inputB.get(x, y));
for each entry in the matrices.

JRR
  • 66
  • 4
  • Adding two sparse matrices can be done in o(n), full blown addition n^2 – xyz Jul 09 '15 at 20:10
  • @prakharsingh95 The assertion is not quite true. For adding 2 sparse matrices, it is O(nnz), where nnz is the count of the union of non-zero entries in both matrices. nnz can vary from 0 to n*m, the number of possible entries. The additional cost of modifying sparse matrices after creation can overwhelm the advantage in computation depending on the application. – JRR Jul 09 '15 at 23:30
  • of course. I had somehow kept the special case of diagonal matrices in mind rather than sparse matrix. Nonetheless, my point is that it can indeed be beneficial to have multiple matrix addition definitions. – xyz Jul 10 '15 at 04:20