I created a Matrix template class to work with matrixes of various types (int, float, 2D-points, etc.)
template<typename Type>
class Matrix {
...
};
I added some operators like +, -, *, /. These functions also need to be templated, because I want to multiply a Matrix type by float, or by a Matrix.
Here is my multiplication implementation:
template<typename T>
Matrix operator*(const T &other) const {
Matrix temp(*this);
temp *= other;
return temp;
}
Matrix &operator*=(const Matrix &other) {
auto temp = Matrix(rows, other.columns);
for (int i = 0; i < rows; i++)
for (int j = 0; j < other.columns; j++) {
temp.matrix[i][j] = Type();
for (int k = 0; k < other.rows; k++)
temp.matrix[i][j] += matrix[i][k] * other.matrix[k][j];
}
AllocMatrixData(temp.rows, temp.columns);
matrix = temp.matrix;
return *this;
}
template<typename T>
Matrix &operator*=(const T value) {
for (ProxyVector<Type> &line : matrix) <- ProxyVector is just a wrapper
line *= value;
return *this;
}
I want my Matrix object to be on the left side, so I can do this:
Matrix * 5
However I can't do this:
5 * Matrix
So I added a function that can take a type T as the first argument and Matrix as second.
template<typename T>
friend Matrix<Type> operator*(const T Value, const Matrix<Type> &other)
{
return other * Value;
}
But now Matrix*Matrix multiplication is ambiguous. I think I understand why - I have two functions that take T and Matrix and in this case T can be the Matrix type too. So how do I fix this?
When multiplying 2 matrices, both the member operator and operator*
are applicable and have equal precedence regarding the decision of the overload to choose.
Use concepts (or SFINAE prior to C++20) to determine, if the parameter is a matrix or a scalar. This allows you to define overloads that only take part in overload resolution for the proper parameter types.
I recommend being consistent with the location of the implementation of the operator btw: If you implement a symetric operator where one of the overloads need to be defined at namespace scope, define both overloads at namespace scope; this allows you to keep the logic of both versions close together making it easier to maintain the logic.
In the following case we simply define everything that's not a matrix a scalar.
template<class T>
struct Matrix
{
};
template<class T>
struct IsMatrixHelper : std::false_type {};
template<class T>
struct IsMatrixHelper<Matrix<T>> : std::true_type {};
template<class T>
concept Scalar = !IsMatrixHelper<T>::value;
template<class T, Scalar U>
auto operator*(Matrix<T> const& m, U scalar)
{
return Matrix<decltype(std::declval<T>()* std::declval<U>())>{};
}
template<Scalar T, class U>
auto operator*(T scalar, Matrix<U> const& m)
{
return Matrix<decltype(std::declval<T>()* std::declval<U>())>{};
}
template<class T, class U>
auto operator*(Matrix<T> const& m1, Matrix<U> const& m2)
{
return Matrix<decltype(std::declval<T>()* std::declval<U>())>{};
}
static_assert(std::is_same_v<decltype(std::declval<Matrix<int>>() * std::declval<Matrix<long long>>()), Matrix<long long>>);
static_assert(std::is_same_v<decltype(std::declval<Matrix<long long>>() * std::declval<int>()), Matrix<long long>>);
static_assert(std::is_same_v<decltype(std::declval<long long>() * std::declval<Matrix<int>>()), Matrix<long long>>);
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.