-1

This error is occurring when I am doing scalar multiplication. I am using template classes to perform these operations on matrices. I have been trying to grasp at the concepts, but I seem to be failing. Any help is appreciated.

main.cpp inside main function

    Matrix<int, 3, 3> m4;
    Matrix<int, 3, 3> m5(3);
    m5 = m4 * 2;  //This works
    m5 = 2 * m4;  //Gives an error for this
    cout << m5 << endl;

matrix.h

#include <exception>

template <class T, int M, int N>
Matrix<T, M, N> operator*(T, const Matrix<T, M, N> &);

template <class T, int M, int N>
class Matrix
{
private:
    T mat[M][N];
    int rows = M;
    int cols = N;

public:
    // Error class
    class IllegalOperation : public std::exception
    {
    public:
        IllegalOperation(const char *msg) : _msg(msg) {}
        virtual const char *what() const throw() { return _msg.c_str(); };

    private:
        std::string _msg;
    };
    // constructor
    Matrix(int v = 0)
    {
        for (int i = 0; i < M; i++)
        {
            for (int j = 0; j < N; j++)
                mat[i][j] = v;
        }
    }

    // () overloading
    T &operator()(int i, int j)
    {
        return mat[i][j];
    }

    const T &operator()(int i, int j) const
    {
        return mat[i][j];
    }

    // [] overloading
    T *operator[](int index)
    {
        return mat[index];
    }
    const T *operator[](int index) const
    {
        return mat[index];
    }

    // << overloading
    friend std::ostream &operator<<(std::ostream &os, const Matrix<T, M, N> &L)
    {
        for (int i = 0; i < M; i++)
        {
            for (int j = 0; j < N; j++)
                os << L.mat[i][j] << " ";
            os << "\n";
        }
        return os;
    }

    template <class T1, int M1, int N1>
    Matrix<T, M, N1> operator*(Matrix<T1, M1, N1> const &other);
    template <class T1, int M1, int N1>
    const Matrix<T, M, N1> operator*(Matrix<T1, M1, N1> const &other) const;
    Matrix<T, M, N> operator+(Matrix<T, M, N> const &other);

    // scalar
    Matrix<T, M, N> operator*(T);
    friend Matrix<T, M, N> operator*<>(T scalar, const Matrix<T, M, N> &other);

    friend T min(Matrix obj)
    {
        T result = obj.mat[0][0];
        for (int i = 0; i < M; i++)
        {
            for (int j = 0; j < N; j++)
                if (result < obj.mat[i][j])
                    result = obj.mat[i][j];
        }
        return result;
    }

    long double avg() const
    {
        long double result = 0;
        for (int i = 0; i < M; i++)
        {
            for (int j = 0; j < N; j++)
                if (result < mat[i][j])
                    result = result + mat[i][j];
        }
        return result / (M * N);
    }
};

template <class T, int M, int N>
Matrix<T, M, N> Matrix<T, M, N>::operator+(Matrix const &other)
{
    if ((this->rows == other.rows) && (this->cols == other.cols))
    {
        Matrix<T, M, N> resultantMatrix;

        for (auto i = 0; i < this->rows; i++)
        {
            for (auto j = 0; j < this->cols; j++)
            {
                auto &valueFirst = this->mat[i][j];
                auto &valueSecond = other.mat[i][j];

                // if ((additionOverflow(valueFirst, valueSecond)) || (additionUnderflow(valueFirst, valueSecond)))
                //     throw std::out_of_range("Resultant value of matrix is out of range");
                // else
                resultantMatrix(i, j) = valueFirst + valueSecond;
            }
        }
        return resultantMatrix;
    }
    else
        throw IllegalOperation("Matrices cannot be added, sizes do not match");
}

template <class T, int M, int N>
template <class T1, int M1, int N1>
Matrix<T, M, N1> Matrix<T, M, N>::operator*(Matrix<T1, M1, N1> const &other)
{
    std::cout << M << " " << N1;
    if (N == M1)
    {
        Matrix<T, M, N1> resultantMatrix;

        for (auto i = 0; i < this->rows; i++)
        {
            for (auto j = 0; j < this->cols; j++)
            {
                for (auto k = 0; k < this->cols; k++)
                {
                    auto &valueFirst = this->mat[i][k];
                    auto &valueSecond = other(k, j);

                    // if ((additionOverflow(valueFirst, valueSecond)) || (additionUnderflow(valueFirst, valueSecond)))
                    //     throw std::out_of_range("Resultant value of matrix is out of range");
                    // else
                    resultantMatrix(i, j) += valueFirst * valueSecond;
                }
            }
        }
        return resultantMatrix;
    }
    else
        throw IllegalOperation("Matrices cannot be multipled, sizes not compatible");
}

template <class T, int M, int N>
template <class T1, int M1, int N1>
Matrix<T, M, N1> const Matrix<T, M, N>::operator*(Matrix<T1, M1, N1> const &other) const
{
    if (N == M1)
    {
        Matrix<T, M, N1> resultantMatrix;

        for (auto i = 0; i < this->rows; i++)
        {
            for (auto j = 0; j < this->cols; j++)
            {
                for (auto k = 0; k < this->cols; k++)
                {
                    auto &valueFirst = this->mat[i][k];
                    auto &valueSecond = other(k, j);

                    // if ((additionOverflow(valueFirst, valueSecond)) || (additionUnderflow(valueFirst, valueSecond)))
                    //     throw std::out_of_range("Resultant value of matrix is out of range");
                    // else
                    resultantMatrix(i, j) += valueFirst * valueSecond;
                }
            }
        }
        return resultantMatrix;
    }
    else
        throw IllegalOperation("Matrices cannot be multipled, sizes not compatible");
}

Function for m * 2, and this works

// scalar m * T
template <class T, int M, int N>
Matrix<T, M, N> Matrix<T, M, N>::operator*(T scalar)
{
    Matrix<T, M, N> resultantMatrix;

    for (auto i = 0; i < this->rows; i++)
    {
        for (auto j = 0; j < this->cols; j++)
        {
            auto valueFirst = this->mat[i][j];
            if (scalar == 0)
                resultantMatrix(i, j) = 0;
            // else if ((multiplicationOverflow(valueFirst, scalar)) || (multiplicationUnderflow(valueFirst, scalar)))
            //     throw std::out_of_range("Resultant value of matrix is out of range");
            else
                resultantMatrix(i, j) = this->mat[i][j] * scalar;
        }
    }
    return resultantMatrix;
}

Function for 2 * m, this does not work

template <class T, int M, int N>
Matrix<T, M, N> operator*(T scalar, const Matrix<T, M, N> &other)
{
    Matrix<T, M, N> resultantMatrix;

    for (auto i = 0; i < M; i++)
    {
        for (auto j = 0; j < N; j++)
        {
            auto valueFirst = other(i, j);
            if (scalar == 0)
                resultantMatrix(i, j) = 0;
            // else if ((multiplicationOverflow(valueFirst, scalar)) || (multiplicationUnderflow(valueFirst, scalar)))
            //     throw std::out_of_range("Resultant value of matrix is out of range");
            else
                resultantMatrix(i, j) = other(i, j) * scalar;
        }
    }
    return resultantMatrix;
}

Errors

In file included from main.cpp:3:
matrix.h:4:1: error: ‘Matrix’ does not name a type
    4 | Matrix<T, M, N> operator*(T, const Matrix<T, M, N> &);
      | ^~~~~~
matrix.h: In instantiation of ‘class Matrix<int, 3, 2>’:
main.cpp:10:29:   required from here
matrix.h:76:28: error: template-id ‘operator*<>’ for ‘Matrix<int, 3, 2> operator*(int, const Matrix<int, 3, 2>&)’ does not match any template declaration
   76 |     friend Matrix<T, M, N> operator*<>(T scalar, const Matrix<T, M, N> &other);
      |                            ^~~~~~~~~~~
matrix.h:210:17: note: candidates are: ‘Matrix<T, M, N> Matrix<T, M, N>::operator*(T)’
  210 | Matrix<T, M, N> Matrix<T, M, N>::operator*(T scalar)
      |                 ^~~~~~~~~~~~~~~
matrix.h:71:28: note:                 ‘template<class T, int M, int N> template<class T1, int M1, int N1> const Matrix<T, M, N1> Matrix<T, M, N>::operator*(const Matrix<T1, M1, N1>&) const’
   71 |     const Matrix<T, M, N1> operator*(Matrix<T1, M1, N1> const &other) const;
      |                            ^~~~~~~~
matrix.h:69:22: note:                 ‘template<class T, int M, int N> template<class T1, int M1, int N1> Matrix<T, M, N1> Matrix<T, M, N>::operator*(const Matrix<T1, M1, N1>&)’
   69 |     Matrix<T, M, N1> operator*(Matrix<T1, M1, N1> const &other);
      |                      ^~~~~~~~
matrix.h: In instantiation of ‘class Matrix<int, 3, 3>’:
main.cpp:12:25:   required from here
matrix.h:76:28: error: template-id ‘operator*<>’ for ‘Matrix<int, 3, 3> operator*(int, const Matrix<int, 3, 3>&)’ does not match any template declaration
   76 |     friend Matrix<T, M, N> operator*<>(T scalar, const Matrix<T, M, N> &other);
      |                            ^~~~~~~~~~~
matrix.h:210:17: note: candidates are: ‘Matrix<T, M, N> Matrix<T, M, N>::operator*(T)’
  210 | Matrix<T, M, N> Matrix<T, M, N>::operator*(T scalar)
      |                 ^~~~~~~~~~~~~~~
matrix.h:71:28: note:                 ‘template<class T, int M, int N> template<class T1, int M1, int N1> const Matrix<T, M, N1> Matrix<T, M, N>::operator*(const Matrix<T1, M1, N1>&) const’
   71 |     const Matrix<T, M, N1> operator*(Matrix<T1, M1, N1> const &other) const;
      |                            ^~~~~~~~
matrix.h:69:22: note:                 ‘template<class T, int M, int N> template<class T1, int M1, int N1> Matrix<T, M, N1> Matrix<T, M, N>::operator*(const Matrix<T1, M1, N1>&)’
   69 |     Matrix<T, M, N1> operator*(Matrix<T1, M1, N1> const &other);
Van Wilder
  • 444
  • 4
  • 14

1 Answers1

0

You need to forward declare your class before you use it, like this.

template <class T, int M, int N>
class Matrix; 

template <class T, int M, int N>
Matrix<T, M, N> operator*(T, const Matrix<T, M, N> &); 

template <class T, int M, int N>
class Matrix
{
    ...
};

A simpler option would be to remove the declaration of operator* and just define it after the definition of Matrix. Like this

template <class T, int M, int N>
class Matrix
{
    ...
};

template <class T, int M, int N>
Matrix<T, M, N> operator*(T, const Matrix<T, M, N> &)
{
    ...
}

but maybe that will cause problems elsewhere in your code, I can't see enough of it to tell.

john
  • 85,011
  • 4
  • 57
  • 81
  • I tried both, the first one didn't work but the second one did! Which makes me wonder more about operators. It seems like `2 * m` goes in as function that can be directly overloaded where as `m * 2` goes in as overloading for the template class. Am I correct? But why does the first solution not work? – Van Wilder Jun 12 '22 at 10:02
  • Also, that is all of my code @john. – Van Wilder Jun 12 '22 at 10:02
  • @VanWilder the operator for `2*m` must go in a function because `2` is not an object. With `m*2` you have a choice, either a function or a in a template class. However it's undoubtedly best practice that both should be in a function. The reasons are quite technical and I'm not going to describe them here. But conceptually `operator*` is a symmetric operator which treats both it's arguments the same, so why should you have two different approaches depending on whether the integer is first or second? – john Jun 12 '22 at 10:07
  • Then I can potentially do the same for `m1*m2` or `m1+=m2`, correct? Also, even though it is supposed to be symmetric in nature if I only have 1 definition for it such as `Matrix operator*(T scalar, const Matrix &other)` it does not work for both the cases. That are `2*m` or `m*2`. – Van Wilder Jun 12 '22 at 10:22
  • Yes, but the argument is weaker for `m1+=m2`. Unlike all the other operators in that case `m1` is being treated differently from `m2` (it's the object being modified). And yes you need both version of `operator*` for integers on the left or right hand sides. C++ doesn't require `operator*` to be symmetrical, it's up to you to provide an implementation that is. – john Jun 12 '22 at 10:50
  • You should probably take a read of [this answer](https://stackoverflow.com/questions/4421706/what-are-the-basic-rules-and-idioms-for-operator-overloading) for all the details – john Jun 12 '22 at 10:52
  • One more question, this is on template itself. Say you want to create an object of an object something like `Matrix, 3, 3>` how will that work? I believe, I just have to create a constructor to handle the initialization in such a case, no? – Van Wilder Jun 12 '22 at 11:01
  • It should work exactly like that. If it's not working then that's a new question. – john Jun 12 '22 at 11:02
  • @VanWilder Yes your existing constructor assumes `int`, it should be like this `Matrix(const T& v = T()) { ... }`. – john Jun 12 '22 at 11:04
  • I tested it out, it works perfectly fine. I assume this is how complex software must be created because all this is getting complicated too aha. – Van Wilder Jun 12 '22 at 11:05