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>>);
Related
I am writing a matrix class template. I want to implement the multiplication or addition of different types of matrices( for example, between real and imaginary matrices ), but I cannot access the data members of another type of matrix. How should I solve this problem?
template <class Type>
class Matrix
{
Type** p_data; //Represents matrix data
int row, col; //Represents the number of rows and columns of the matrix
public:
Matrix(int r, int c);
~Matrix();
Matrix(Type *data, int row, int col);
Type* operator[] (int i);
// Overload [], for Matrix object M, can be accessed by M [I] [J] to
// access the i + 1 line, the J + 1 column element
Matrix &operator = (const Matrix& m);
// Overload =, implement matrix overall assignment, if the row / column does not wait,
// return space and reassign
bool operator == (const Matrix& m) const; //Overload ==, determined whether the matrix is equal
template <class Type1>
Matrix operator + (const Matrix<Type1>& m) const;
// Overload +, complete matrix addition, can assume
// that the two matrices meet the addition conditions (the rows, the columns are equal)
template <class Type1>
Matrix operator * (const Matrix<Type>& m) const;
// Overload *, complete matrix multiplication, can assume that two matrices meet multiplication
// conditions (this.col = m.row)
template <class Type1>
friend std::ostream& operator << (std::ostream& out,Matrix<Type1>& c);
};
template<class Type>
template <class Type1>
Matrix<Type> Matrix<Type>::operator + (const Matrix<Type1>& m) const{
Matrix<Type> res(row,col);
for(int i=0;i<row;++i)
for(int j=0;j<col;++j)
res.p_data[i][j] =p_data[i][j] + m.p_data[i][j]; // error in this line
return res;
}
You can use a friend declaration
template <typename T>
class Matrix
{
template <typename U>
friend class Matrix;
// ...
};
I'm having some problem with my std::forward constructor for my template "matrix" class. Basically i want to set a matrix of type float and size 4 equal to the sum of 2 matrices of type float and size 3. I do this inside of my struct 'matrix_struct' in the function 'test'. However, MSVC error tells me that "'static_cast': cannot convert from 'matrix' to 'float'" and whenever I inspect the error it takes me to the 3rd matrix constructor with std::forward.
///////////////////////////////////
somefile.hpp
#pragma once
#include "matrix.hpp"
using matrix3 = matrix<float, 3>;
using matrix4 = matrix<float, 4>;
struct matrix_struct {
matrix4 sum;
void test(const matrix3& a, const matrix3& b)
{
sum = a + b;
}
}
///////////////////////////////////
matrix.hpp
#pragma once
#include <array>
template <typename t, size_t dim>
class matrix
{
public:
matrix() { data.fill(static_cast<t>(0) }
explicit matrix(const std::array<t, dim>& a) : data(a) {}
template <typename... args_t>
matrix(args_t... args) : data{ static_cast<t>(std::forward<args_t>(args))... } }
public:
t& at(const size_t index)
{
return data.at(index >= dim ? dim - 1 : index);
}
const t& at(const size_t index) const
{
return data.at(index >= dim ? dim - 1 : index);
}
public:
matrix& operator = (const matrix<t, dim>& other)
{
for (size_t i = 0; i < dim; ++i) {
at(i) = other.at(i);
}
return *this;
}
matrix& operator = (const std::array<t, dim>& other)
{
for (size_t i = 0; i < dim; ++i) {
at(i) = other.at(i);
}
return *this;
}
matrix& operator = (const t& other)
{
for (size_t i = 0; i < dim; ++i) {
at(i) = other;
}
return *this;
}
public:
matrix operator + (const matrix<t, dim>& other) const
{
matrix<t, dim> ret;
for (size_t i = 0; i < dim; ++i) {
ret.at(i) = at(i) + other.at(i);
}
return ret;
}
matrix operator + (const std::array<t, dim>& other) const
{
matrix<t, dim> ret;
for (size_t i = 0; i < dim; ++i) {
ret.at(i) = at(i) + other.at(i);
}
return ret;
}
matrix operator + (const t& other) const
{
matrix<t, dim> ret;
for (size_t i = 0; i < dim; ++i) {
ret.at(i) = at(i) + other;
}
return ret;
}
private:
std::array<t, dim> data;
};
Template constructors are problematic. They often create code that is a better candidate than your other constructors.
The general solution is to disable the template if its decayed type matches the class you are writing.
example:
struct MyClass
{
template
<
class Arg,
class...Rest,
std::enable_if_t
<
! std::is_same
<
std::decay_t<Arg>,
MyClass
>::value
>* = nullptr
>
MyClass(Arg&& arg, Rest&&...rest)
{
// code to construct from something that's not MyClass
// this will no longer hijack copy constructors etc.
}
};
The first problem of your code sample is addressed by #RichardHodges's answer.
Assuming you include his solution to overcome tricky copy/move constructor selection, another problem remains: you do not offer a matrix promotion/demotion service through your constructors/assignment operators.
Therefore, the following line in your test function:
sum = a + b; // a + b is a matrix<float, 3>, sum a matrix<float, 4>
Will trigger a call to the variadic template constructor and fail.
Starting from Richard's solution, you need to tweak a bit the SFINAE condition to extend it to matrices of any size. To do so, we will need a little is_matrix trait:
template <typename T, size_t Dim>
class matrix;
template <typename T>
struct is_matrix : std::false_type {};
template <typename Num, size_t Size>
struct is_matrix<matrix<Num, Size> > : std::true_type {
using value_type = Num;
};
Now the variadic template constructor becomes:
template <typename t, size_t dim>
class matrix
{
/* ... */
public:
/* ... */
template
<
class Arg,
class...Rest,
std::enable_if_t
<
! std::is_matrix
<
std::decay_t<Arg>
>::value
>* = nullptr
>
matrix(Arg&& arg, Rest&&...rest)
{
// code to construct from something that's not a matrix
// this will no longer hijack copy constructors etc.
}
};
Then, we need to add the proper matrix constructor along with the proper friend declaration:
template <typename t, typename dim>
class matrix {
public:
template <typename OtherT, size_t OtherDim>
friend class matrix;
template <size_t OtherDim>
matrix(matrix<t, OtherDim> const& other) {
size_t i = 0;
for (; i < min(OtherDim, dim); ++i) {
data[i] = other.data[i];
}
for(; i < dim; ++i) {
data[i] = t();
}
}
template <typename OtherT,
size_t OtherDim>
matrix(matrix<OtherT, OtherDim> const&) {
static_assert(std::is_same<t, OtherT>::value,
"value_type mismatch between matrices!");
}
/* ... */
};
Note: You need the friend declaration because matrix<Type1, Dim1> and matrix<Type2, Dim2> are completely different types whenever Type1 != Type2 or Dim1 != Dim2 and as such, you cannot access matrix<OtherT, OtherDim>'s private/protected members in matrix<t, dim> without that friend declaration.
This implementation will initialize the target matrix by filling its data member with the content of the given matrix when the value types match:
If the given matrix is bigger, it will be truncated.
If the given matrix is smaller, the remaining elements will be 0 initialized
If the value types don't match, the less specialized matrix<OtherT, OtherDim> constructor is the only available overload and it triggers a compiler error through a static_assert.
You would also need to define the equivalent assigment operators... Which I left as exercises.
A demo of these constructors in action can be found on Coliru
I have a 3x3 Matrix and a 3x1 Vector classes. I have two multiplication operators; one for multiplying a matrix with a scalar, another for multiplying a matrix with a vector object. The matrix-scalar multiplication operator is member inside the Matrix class and the matrix-vector multiplication operator is global.
#include <initializer_list>
#include <array>
template <class T>
class Matrix
{
public:
Matrix(std::initializer_list<T> List);
Matrix() : Matrix({0,0,0,0,0,0,0,0,0}) {}
template <class S> // THE COMPILER TRIES TO USE
Matrix<T> operator*(const S & Scalar); // THIS OPERATOR IN BOTH CASES.
const T & operator()(size_t i, size_t j) const;
private:
static constexpr size_t SIZE = 3;
static constexpr size_t AREA = SIZE * SIZE;
std::array<T, AREA> E;
};
template <class T>
Matrix<T>::Matrix(std::initializer_list<T> List)
{
if (List.size() != AREA) throw("Error!");
for (size_t i=0; i<AREA; i++)
{
E[i] = *(List.begin() + i);
}
}
template <class T>
const T & Matrix<T>::operator()(size_t i, size_t j) const
{
return E[SIZE * j + i];
}
template <class T>
template <class S>
Matrix<T> Matrix<T>::operator*(const S & Scalar)
{
const T ScalarT = static_cast<T>(Scalar);
Matrix<T> Result;
for (size_t i=0; i<AREA; i++)
{
Result.E[i] = E[i] * ScalarT;
}
return Result;
}
template <class T>
class Vector
{
public:
Vector(std::initializer_list<T> List);
Vector() : Vector({0,0,0}) {};
const T & operator()(size_t i) const;
T & operator()(size_t i);
private:
static constexpr size_t SIZE = 3;
std::array<T, SIZE> E;
};
template <class T>
Vector<T>::Vector(std::initializer_list<T> List)
{
if (List.size() != SIZE) throw("Error!");
for (size_t i=0; i<SIZE; i++)
{
E[i] = *(List.begin() + i);
}
}
template <class T>
const T & Vector<T>::operator()(size_t i) const
{
return E[i];
}
template <class T>
T & Vector<T>::operator()(size_t i)
{
return E[i];
}
template <class T> // THE COMPILER NEVER TRIES USING THIS GLOBAL OPERATOR.
Vector<T> operator*(const Matrix<T> & Mat, const Vector<T> & Vec)
{
Vector<T> Result;
Result(0) = Mat(0,0) * Vec(0) + Mat(0,1) * Vec(1) + Mat(0,2) * Vec(2);
Result(1) = Mat(1,0) * Vec(0) + Mat(1,1) * Vec(1) + Mat(1,2) * Vec(2);
Result(2) = Mat(2,0) * Vec(0) + Mat(2,1) * Vec(1) + Mat(2,2) * Vec(2);
return Result;
}
int wmain(int argc, wchar_t *argv[]/*, wchar_t *envp[]*/)
{
Matrix<float> Mat1({2, 0, 0,
0, 2, 0,
0, 0, 2});
Vector<float> Vec1({1,
2,
3});
Matrix<float> Mat2 = Mat1 * 2; // Matrix-Scalar Multiplication
Vector<float> Vec2 = Mat1 * Vec1; // Matrix-Vector Multiplication
return 0;
}
The problem is, when I try to do a matrix-vector multiplication, the compiler chooses and tries to use the matrix-scalar multiplication operator, and gives a compiler error.
If I delete the matrix-scalar multiplication operator and the line where I use it, the program runs successfully. In reverse, if I delete the matrix-vector multiplication operator, it again runs successfully. They just don't get along. And when it runs (in either case), it makes the all calculations correctly.
What is going wrong here?
Compiler & IDE: Microsoft Visual Studio 2015 Community Edition
The language doesn't specify that when searching for overloads to examine all possible scopes. As soon as one candidate is found, only the overloads in that scope are used to select the best candidate. If that candidate fails to compile for other reasons, it still won't examine additional scopes.
So in your case since the scalar is a template parameter it will match any type so as long as the left-hand operand of the operator is a Matrix<T>, the scalar multiplication will match any right-hand operand.
You could:
Just make both operator* at the same scope so they're both considered for overload resolution.
Change the scalar operator to take for example a Scalar<T> that's a thin wrapper around a scalar type rather than a generic template type.
I'm trying to do overload to * operator in my Matrix class.
I have one that make it if it is Matrix*something, (int, double...)
i'm searching for one that make it for the opposite side i.e something*Matrix
this is what i tried
template<class T>
bool operator*(Matrix<T>& other ){
Matrix<T> mat(other.rows,other.columns);
for(int i=0;i<other.rows;i++){
for(int j=0;j<other.columns;j++){
T temp=other.get(i,j);
temp=temp*(this);
mat.set(i,j,temp);
}
}
return mat;
}
and this is what works for Matrix*something
Matrix<T>& operator*(const T & num){
Matrix<T> mat(rows,columns);
for(int i=0;i<rows;i++){
for(int j=0;j<columns;j++){
T temp=(matrix[i][j]);
temp=temp*num;
mat.set(i,j,temp);
}
}
return mat;
}
You should make it a non-member, that is you write outside of Matrix class:
template<class T>
Matrix<T> operator*(const T& num, const Matrix<T>& mat) {
return mat * num; // invoke existing implementation of Matrix * something
}
Note that operator* should return result by value. There is a bug in your implementation, you return dangling reference to a local variable mat.
Note that this form requires num to be of type T so if, like in your example, you have
Matrix<Rational> mat;
mat = 3 * mat;
it won't compile because 3 is not Rational.
What you can do is use identity trick to put num parameter in non-deduced context, so it will be converted from int to Rational:
template<class T>
Matrix<T> operator*(typename boost::mpl::identity<T>::type const& num, const Matrix<T>& mat) {
return mat * num; // invoke existing implementation of Matrix * something
}
Where identity is just
template<typename T>
struct identity { typedef T type; };
Or you can do just
template<class T, class U>
Matrix<T> operator*(const U& num, const Matrix<T>& mat) {
return mat * num; // invoke existing implementation of Matrix * something
}
I am using Boost-Operatators to construct a matrix class. (A toy project). However, I run into issues when I want to mix matrices of different element types.
Basically I have a template class Matrix<T>, where T is the element type of that matrix. I'm using Boost-Operators to define operators between instances of Matrix<T> (e.g. element-wise add), between Matrix<T> and T (e.g. scalar multiplication), and if possible also between Matrix<T> and Matrix<U> (e.g. real matrix plus complex matrix).
The boost operators support one, or two template arguments. One if you want operators between two objects of the same type, and two if you want mixed operators.
template<typename T>
class Matrix : boost::addable<Matrix<T>> // Add another matrix of same type.
boost::multiplyable2<Matrix<T>,T> // Scalar multiplication with a `T`.
However, I cannot give Matrix<U> as a second argument, because then my class would have two template arguments and the type would depend on which matrices I can operate with.
template<typename T, typename U>
class Matrix : boost::addable2<Matrix<T,U>,Matrix<U,?>> // Now I have two template arguments.
// That's certainly not what I want!
I also tried implementing my own version of boost::addable, but this didn't work either. The compiler complains about an uncomplete type.
template<class Derived>
class Addable {
template<class Other>
friend Derived operator+(Derived lhs, const Other &rhs) {
return lhs += rhs;
}
template<class Other>
friend Derived operator+(const Other &lhs, Derived rhs) {
return rhs += lhs;
}
};
Another approach was to define a cast constructor from Matrix<U> to Matrix<T>. However, now I have the issue, that those are two different types, and I don't get access to the private members. So, I either need to make more stuff public than I want to, or find a different way of doing this.
How would you implement such a thing?
The full Code
#include <cassert>
#include <utility>
#include <complex>
#include <vector>
#include <algorithm>
#include <iostream>
#include <boost/operators.hpp>
typedef double Real;
typedef std::complex<Real> Complex;
template<typename T>
class Matrix : boost::addable<Matrix<T>>
{
public:
Matrix() = default;
template<typename U>
Matrix(const Matrix<U> &other)
: m_(other.m()), n_(other.n()),
data_(other.data_.begin(), other.data_.end()) { }
Matrix(size_t m, size_t n) : m_(m), n_(n), data_(m*n) { }
Matrix(size_t m, size_t n, const T &initial)
: m_(m), n_(n), data_(m*n, initial) { }
size_t m() const { return m_; }
size_t n() const { return n_; }
size_t size() const {
assert(m_*n_ == data_.size());
return data_.size();
}
const T &operator()(size_t i, size_t j) const { return data_[i*m_ + j]; }
T &operator()(size_t i, size_t j) { return data_[i*m_ + j]; }
void fill(const T &value) {
std::fill(data_.begin(), data_.end(), value);
}
Matrix &operator+=(const Matrix &other) {
assert(dim_match(other));
for (int i = 0; i < size(); ++i) {
data_[i] += other.data_[i];
}
return *this;
}
friend std::ostream &operator<<(std::ostream &o, const Matrix &m) {
if (m.size() == 0) {
o << "()" << std::endl;
return o;
}
for (int i = 0; i < m.m(); ++i) {
o << "( ";
for (int j = 0; j < m.n() - 1; ++j) {
o << m(i,j) << ", ";
}
o << m(i, m.n() - 1) << " )" << std::endl;
}
return o;
}
private:
bool dim_match(const Matrix &other) {
return n_ == other.n_ && m_ == other.m_;
}
private:
int m_, n_;
typedef std::vector<T> Store;
Store data_;
};
int main() {
Matrix<Real> A(2,3, 1.);
Matrix<Complex> B(2,3, Complex(0,1));
auto C = Matrix<Complex>(A) + B;
std::cout << A << std::endl;
std::cout << B << std::endl;
std::cout << C << std::endl;
}
This is how I'd do it: use a friend template function (see Operator overloading: The Decision between Member and Non-member):
template<typename T>
class Matrix
{
public:
template<typename> friend class Matrix;
And then later
template <typename T1, typename T2>
Matrix<typename std::common_type<T1, T2>::type>
operator+(Matrix<T1> const& a, Matrix<T2> const& b)
{
Matrix<typename std::common_type<T1, T2>::type> result(a);
return (result += b);
}
Note the use of common_type to arrive at a sensible resulting type (you might want to introduce your own trait there to cater for your specific requirements)
See it Live On Coliru