I am building a matrix library and I am trying to use the policy-based design. So my base classes are classes that provide a storage method and some access functions. I also have a function matrix which provides the mathematical functions. This works great, but there is a major problem with the operator* because of the return type. I will explain it with some code.
Base class that provides a stack storage :
template < typename T, unsigned int rows, unsigned int cols>
class denseStackMatrix {
public:
typedef T value_type;
private:
value_type grid[rows][cols];
const unsigned int rowSize;
const unsigned int colSize;
Then I have my matrix class which provides mathematical functionality :
template <typename MatrixContainer >
class matrix : public MatrixContainer {
public:
typedef MatrixContainer Mcontainer;
matrix<Mcontainer>& operator +(const matrix<Mcontainer>&);
matrix<Mcontainer>& operator *(const matrix<Mcontainer>&);
operator+
always works, operator*
only works for square matrix.
So we still need one for all matrices. And that's were it goes
wrong. I have already tried few things, but nothings works.
I look for something like this, with the help of c++0x (usage of
c++0x is not a requirement)
you shall notice the "???" :)
friend auto operator * (const matrix<T1>& matrix1, const matrix<T2>& matrix2)
-> decltype(matrix<???>);
An example of the problem
matrix<denseStackMatrix<int,3,2> > matrix1;
matrix<denseStackMatrix<int,2,4> > matrix2;
matrix<denseStackMatrix<int,3,4> > matrix3 = matrix1 * matrix2;
Here it will complain about the type, because it does not match any of the two parameter types. But the compiler needs to know the type at compile-time and I do not know how to provide it.
I know there are other options for the design, but I am really looking for a solution for this scenario..
Thank you !
In C++14, you can just use auto as a return type.
Template argument deduction is used when selecting user-defined conversion function template arguments. A is the type that is required as the result of the conversion. P is the return type of the conversion function template.
While C++ does not have an official way to return multiple values from a function, one can make use of the std::pair , std::tuple , or a local struct to return multiple values.
The return type, which specifies the type of the value that the function returns, or void if no value is returned. In C++11, auto is a valid return type that instructs the compiler to infer the type from the return statement. In C++14, decltype(auto) is also allowed.
Picking up on the idea of @hammar, but with partial specialization to allow the normal syntax like the question shows:
template<class MatrixContainer>
class matrix;
template<
template<class,int,int> class MatrixContainer,
class T, int rows, int cols
>
class matrix< MatrixContainer<T,rows,cols> >{
typedef MatrixContainer<T,rows,cols> Mcontainer;
typedef matrix<Mcontainer> this_type;
static int const MyRows = rows;
static int const MyCols = cols;
public:
template<int OtherCols>
matrix<MatrixContainer<T,MyRows,OtherColls> > operator*(matrix<MatrixContainer<T,MyCols,OtherCols> > const& other){
typedef matrix<MatrixContainer<T,MyCols,OtherCols> > other_type;
typedef matrix<MatrixContainer<T,MyRows,OtherCols> > result_type;
// ...
}
};
Edit: As you said in your comment, you can also use this to create a matrix that doesn't use a MatrixContainer which has row and column size as template parameters:
template<
template<class> class MatrixContainer,
class T
>
class matrix< MatrixContainer<T> >{
typedef MatrixContainer<T> Mcontainer;
typedef matrix<Mcontainer> this_type;
public:
// normal matrix multiplication, return type is not a problem
this_type operator*(this_type const& other){
// ensure correct row and column sizes, e.g. with assert
}
// multiply dynamic matrix with stack-based one:
template<
template<class,int,int> class OtherContainer,
int Rows, int Cols
>
this_type operator*(matrix<OtherContainer<T,Rows,Cols> > const& other){
// ensure correct row and column sizes, e.g. with assert
}
};
Usage:
// stack-based example
matrix<DenseStackMatrix<int,3,2> > m1;
matrix<DenseStackMatrix<int,2,4> > m2;
matrix<DenseStackMatrix<int,3,4> > m3 = m1 * m2;
// heap-based example
matrix<DenseHeapMatrix<int> > m1(3,2);
matrix<DenseHeapMatrix<int> > m2(2,4);
matrix<DenseHeapMatrix<int> > m3 = m1 * m2;
How about changing MatrixContainer
to be a template template parameter?
template <class T, int Rows, int Cols>
class DenseStackMatrix {
public:
typedef T value_type;
private:
value_type grid[Rows][Cols];
};
template <class T, int Rows, int Cols, template<class, int, int> class MatrixContainer>
class Matrix : public MatrixContainer<T, Rows, Cols> {
public:
template <int ResultCols>
Matrix<T, Rows, ResultCols, MatrixContainer> & operator*(const Matrix<T, Cols, ResultCols, MatrixContainer> &);
};
int main() {
Matrix<int, 3, 2, DenseStackMatrix> matrix1;
Matrix<int, 2, 4, DenseStackMatrix> matrix2;
Matrix<int, 3, 4, DenseStackMatrix> matrix3 = matrix1 * matrix2;
}
This way you not only get compile time dimensions checking, but you can also extend this to allow multiplications between matrices of different container types.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With