Imago
Imago

Reputation: 501

Can one change templates arguments?

I have written the following Matrix class:

template <typename T, size_t r, size_t c> //r=rows,c=cols of the Matrix
class Matrix {
public:
    size_t row = 0;
    size_t col = 0;
    T *data;

    template <size_t L>
    Matrix<T, r, L> operator*(const Matrix<T, c, L> &other) const;

    template <size_t r, size_t c>
    Matrix &operator=(const Matrix<T, r, c> &other)

    // ...
};

I overrode some operators to do some basic arithmetic and everything seems to work properly - however there is an issue which I don't know how to address properly: Given the following lines of code:

  Matrix<double, 1, 4> m1(1.2);
  Matrix<double, 4, 1> m2(1.2);
  Matrix<double, 2, 2> m3; // problematic line
  m3 = m1 * m2;

m3 is of type Matrix<double, 2, 2>, is correctly computed, has one row and one col and carries the 5.76, but stays as Matrix<double, 2, 2>. The change of its number of rows and cols is not reflected in its template parameters. Naturally however I would expect the type to be also informative regarding its content.

I don't suppose one cannot turn a Matrix<double, 2, 2> suddenly into a Matrix<double, 1, 1> Matrix, but maybe there is a good solution I just cannot think of now.

And replace:

 template <size_t r, size_t c> void replace(const Matrix<T, r, c> &other) {
    delete[] data; 
    row = other.row; //number of rows of a matrix
    col = other.col; //number of cols of a matrix
    data = new T[col * row]; // data contains all the elements of my matrix
    for (size_t i = 0; i < row * col; i++)
      data[i] = other.data[i];
  }

Upvotes: 1

Views: 109

Answers (1)

YSC
YSC

Reputation: 40060

From your declaration

template <typename T, size_t r, size_t c> //r=rows,c=cols of the Matrix
class Matrix {
public:
    template <size_t L>
    Matrix<T, r, L> operator*(const Matrix<T, c, L> &other) const;

    template <size_t r, size_t c> // BEWARE: shadowing
    Matrix &operator=(const Matrix<T, r, c> &other);

    // ...
};

we can guess what happens.

Matrix<double, 1, 4> m1(1.2);
Matrix<double, 4, 1> m2(1.2);
m1 * m2; // (1)

(1) calls Matrix<double, 1, 4>::operator*<1>(Matrix<double, 4, 1> const&). It result has then type Matrix<double, 1, 1>.

Matrix<double, 2, 2> m3;
m3 = /* (2) */ m1 * m2;

(2) calls Matrix<double, 2, 2>::operator=<1, 1>(Matrix<double, 1, 1> const&). This is a problem.

A solution would be to ensure operator= can only be called with another matrix of the right size:

template <typename T, size_t r, size_t c> //r=rows,c=cols of the Matrix
class Matrix {
public:
    template <size_t L>
    Matrix<T, r, L> operator*(Matrix<T, c, L> const& other) const;

    Matrix &operator=(Matrix const& other);

    // ...
};

You could even allow type conversions:

template<class U>
Matrix &operator=(Matrix<U, r, c> const& other);

Finally, you might want to use auto:

Matrix<double, 1, 4> m1(1.2);
Matrix<double, 4, 1> m2(1.2);
auto m3 = m1 * m2; // m3 is Matrix<double, 1, 1>

Upvotes: 3

Related Questions