Ben1980
Ben1980

Reputation: 339

How to fix 'no match for ‘operator*’ (operand types are ‘Matrix<double, 2, 3>’ and ‘Matrix<double, 3, 2>’)' with templates

I'm trying to implement a template based Matrix class in C++17. The class should override operator*() to allow formulations such as

Matrix<double, 2, 3> a = {{{3,2,1},{1,0,2}}};
Matrix<double, 3, 2> b = {{{1,2},{0,1},{4,0}}};
Matrix c = a * b;

but this is causing a compiler error.

no match for ‘operator*’ (operand types are ‘Matrix<double, 2, 3>’ and ‘Matrix<double, 3, 2>’)
   43 |     Matrix c = a * b;
      |                ~ ^ ~
      |                |   |
      |                |   Matrix<[...],3,2>
      |                Matrix<[...],2,3>

I know in principle Matrix a and b are completely different types but somehow I think it should be possible because both, 2nd and 3rd, template arguments are of type size_t.

template <typename T, size_t nbRows, size_t nbColumns>
class Matrix {
public:
    Matrix(std::vector<std::vector<T>> &&vector) {
        assert(vector.size() == nbRows);
        assert(vector.front().size() == nbColumns);

        auto matrixIter = matrix.begin();
        for(auto && row : vector) {
            std::copy_n(std::make_move_iterator(row.begin()), nbColumns, matrixIter);
            std::advance(matrixIter, nbColumns);
        }
    }

    friend inline Matrix operator*(Matrix lhs, const Matrix &rhs) {
        // Compute the product of lhs and rhs
        return lhs;
    }

private:
    std::array<T, nbRows * nbColumns> matrix;
};

Upvotes: 2

Views: 3993

Answers (4)

vasama
vasama

Reputation: 81

Your operator* applies to Matrix<A, B> and Matrix<A, B> (same types) but not Matrix<A, B> and Matrix<B, A> (different types). Instead of defining the operator within the class template, do it as a free function outside of the class template like so:

template<typename T, size_t LhsRows, size_t LhsColumns, size_t RhsRows, size_t RhsColumns>
auto operator*(const Matrix<T, LhsRows, LhsColumns>& lhs, const Matrix<T, RhsRows, RhsColumns>& rhs)
{
    static_assert(LhsColumns == RhsRows, "unrelated matrix sizes");

    // ...
}

This way you can apply the operator to two different instantiations of the Matrix class template.

Upvotes: 4

Gerard097
Gerard097

Reputation: 815

As you said a & b have different types Matrix<double, 2, 3> and Matrix<double, 3, 2>

so your operator overload will become something like this

friend inline Matrix<double, 2, 3> operator*(Matrix<double, 2, 3> lhs, const Matrix<double, 2, 3> &rhs) {
    // Compute the product of lhs and rhs
    return lhs;
}

that is the reason of why the compiler cannot find the proper operator overload for your class, so in order to solve this you should add an overload where rhs columns are equal to lhs rows but rhs has a different rows count

template<size_t nbColsR>
friend inline Matrix<T, nbRows, nbColsR> operator*(Matrix lhs, const Matrix<T, nbColumns, nbColsR> &rhs) {
        // Compute the product of lhs and rhs
        return lhs;
}

Upvotes: 0

Fran&#231;ois Andrieux
Fran&#231;ois Andrieux

Reputation: 29022

The two arguments for your operator have different types. You need to introduce a template argument for the number of columns in the second argument and the number of columns in the resulting type. You can then replace Matrix with the proper type for each use :

template<size_t nbOther>
friend inline Matrix<T, nbRows, nbOther> operator*(
    const Matrix<T, nbRows, nbColumns> & lhs,
    const Matrix<T, nbColumns, nbOther> &rhs)
{
    // Do math
}

Since matrix multiplication requires that the inner dimension are the same, you only need 1 additional template argument. Any other information you need is already provided by the class template arguments.

As a final note, operator* should not return lhs. Normally the operator*= variant of the operator would return a reference to lhs but you won't be able to implement it here since the operation would need to change the type of lhs which is not possible in c++. The return value will be a distinct matrix of type Matrix<T, nbRows, nbOther> which generally wont be the same type as either of the parameters.

Upvotes: 2

Some programmer dude
Some programmer dude

Reputation: 409166

Make the operator* function a template, that takes other sets of "rows" and "columns":

template<size_t rows1, size_t cols1, size_t rows2, size_t cols2>
friend inline Matrix operator*(Matrix<T, rows1, cols1> lhs, const Matrix<T, rows2, cols2> &rhs) {
    // Compute the product of lhs and rhs
    return lhs;
}

Or if you only want to handle the reverse (switching "rows" and "columns") then use that for the classes in the argument list:

friend inline Matrix operator*(Matrix<T, nbRows, nbColumns> lhs, const Matrix<T, nbColumns, nbRows> &rhs) {
    // Compute the product of lhs and rhs
    return lhs;
}

Upvotes: 2

Related Questions