indigoblue
indigoblue

Reputation: 483

Operator overload error in class template due to template arguments not being constant expressions

I'm writing a simple matrix class that uses a template to define the datatype, as well as the number of rows and columns. However, I'm running into some issues when defining operator overloads. Here is the class so far:

template<typename T, size_t num_rows, size_t num_cols>
class Matrix {
    public:
        Matrix();
        Matrix(const Matrix &m); // Copy constructor
        ~Matrix();

        T operator()(const size_t row, const size_t col) const;
        T& operator()(const size_t row, const size_t col);
        Matrix& operator=(const Matrix &m); // Copy assignment operator
        Matrix operator*(const Matrix &m2) const;

    private:
        T data_[num_rows][num_cols];
        size_t num_rows_;
        size_t num_cols_;
};

template<typename T, size_t num_rows, size_t num_cols>
Matrix<T, num_rows, num_cols>::Matrix() : num_rows_(num_rows), num_cols_(num_cols) {}

template<typename T, size_t num_rows, size_t num_cols>
Matrix<T, num_rows, num_cols>::~Matrix() {}

// Copy constructor
template<typename T, size_t num_rows, size_t num_cols>
Matrix<T, num_rows, num_cols>::Matrix(const Matrix &m) :
    num_rows_(m.num_rows_),
    num_cols_(m.num_cols_)
{    
    for(size_t i = 0; i < this->num_rows_; ++i) {
        for(size_t j = 0; j < this->num_cols_; ++j) {
            (*this)(i,j) = m(i,j);
        }
    }
}

template<typename T, size_t num_rows, size_t num_cols>
T Matrix<T, num_rows, num_cols>::operator()(const size_t row, const size_t col) const {
    return data_[row][col];
}

template<typename T, size_t num_rows, size_t num_cols>
T& Matrix<T, num_rows, num_cols>::operator()(const size_t row, const size_t col) {
    return data_[row][col];
}

// Copy assignment operator
template<typename T, size_t num_rows, size_t num_cols>
Matrix<T, num_rows, num_cols>& Matrix<T, num_rows, num_cols>::operator=(const Matrix &m) {
    if(this == &m){
        return *this;
    }

    for(size_t i = 0; i < this->num_rows_; ++i) {
        for(size_t j = 0; j < this->num_cols_; ++j) {
            (*this)(i,j) = m(i,j);
        }
    }

    return *this;
}

// Multiplication operator overload
template<typename T, size_t num_rows, size_t num_cols>
Matrix<T, num_rows, num_cols> Matrix<T, num_rows, num_cols>::operator*(const Matrix &m2) const {

    const size_t rows = this->num_rows_;
    const size_t cols = m2.num_cols_;

    Matrix<float, rows, cols> m3;
    for(size_t i = 0; i < this->num_rows_; ++i) {
        for(size_t j = 0; j < m2.num_cols_; ++j) {
            m3(i,j) = 0.0f;
            for(size_t k = 0; k < this->num_cols_; ++k) {
                m3(i,j) += (*this)(i,k)*m2(k,j);
            }
        }
    }

    return m3;
}

Looking at the multiplication operator overload (please forgive the very slow matrix-matrix product implementation), the problem occurs when I try and define Matrix<float, rows, cols> m3. The error I get says error: non-type template argument is not a constant expression, which occurs because the dimensions of m3 are dependent on the dimensions of this and m2. However, it seems that template arguments have to be known at compile time, and therefore I can't use const size_t rows = this->num_rows_ and const size_t cols = m2.num_cols_ when instantiating m3.

As such, I'm not too sure how I can get the multiplication operator overload to work (and other operator overloads that require returning a new matrix as the result of the operation), since I'm unable to create a matrix to return. Is there a way to keep the current template (i.e. datatype, rows and cols), and still get the multiplication operator overload to work?

Upvotes: 0

Views: 73

Answers (2)

Yakk - Adam Nevraumont
Yakk - Adam Nevraumont

Reputation: 275966

Matrix operator*(const Matrix &m2) const;

this is wrong.

template<std::size_t Y>
Matrix<T, num_rows,Y> operator*(const Matrix<T, num_cols, Y> &m2) const;

this is correct..

Also, delete

    size_t num_rows_;
    size_t num_cols_;

or make them constexpr or enum constants.

The template multiplication operator will have the 3 dimensions as part of its template arguments.

// Multiplication operator overload
template<typename T, size_t num_rows, size_t num_cols>
template<size_t Y>
Matrix<T, num_rows, Y> Matrix<T, num_rows, num_cols>::operator*(const Matrix<T,num_cols,Y> &m2) const {

you know the cols/rows of each variable from their types.

Upvotes: 0

John Zwinck
John Zwinck

Reputation: 249642

Make these two member variables static constexpr and initialize them from the template arguments, since they are just names for the template arguments there's no reason to store them in memory, they are known at compile time:

    size_t num_rows_;
    size_t num_cols_;

Also note that your copy constructor (and probably assignment operator) are pointless, the compiler would do the job for you if you didn't declare those.

Upvotes: 0

Related Questions