Reputation: 339
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
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
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
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
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