I'm trying to shape up my template skills (I know very little) by creating a library containing matrices and operations on those matrices. Basically, I want my matrix to be very strongly typed (datatype and size known at compile-time) and I also want to be able to automatically deduct the type of the transpose matrix.
template< typename TDataType, size_t rows, size_t cols > class MyMatrix
Matrices can be nested, so TDataType
can be an integral type, but also a MyMatrix<...>
by itself, causing the datatype for a transposed matrix to be not necessarily the same as the one of the original matrix, e.g:
Transpose( MyMatrix< MyMatrix< char, 2, 3 >, 4, 6 > ) ==> MyMatrix< MyMatrix< char, 3, 2 >, 6, 4 >
(the datatype of the outer matrix has changed)
My first attempt for transposed-type-deduction was:
template< typename TDataType >
struct Transpose
{
typedef TDataType type;
};
template<>
struct Transpose< MyMatrix<TDataType, rows, cols> >
{
typedef MyMatrix<typename Transpose<TDataType>::type, cols, rows> type;
};
I have found no way of doing this because I can't seem to specialize the Transpose-template with MyMatrix (TDataType unknown and similar errors).
The only compilable solution I've come up with (I don't even know if it works yet) is this:
template< typename TMatrixType, typename TDataType, size_t rows, size_t cols >
struct Transpose
{
typedef TMatrixType type;
};
template< typename TDataType, size_t rows, size_t cols >
struct Transpose< MyMatrix<TDataType, rows, cols>, TDataType, rows, cols >
{
typedef MyMatrix< typename Transpose<TDataType,TDataType,rows,cols>::type, cols, rows > type;
};
I believe I am making things too complicated; is there an easier solution to achieve what I want?
An answer to the answers to my question (I posted the question without an account, so I don't have enough rep to do things the normal way). Thanks a lot already!
@Bo Persson @Will A : I do not intend to use this as a general purpose matrix library, I want to perform operations on matrices of specific (known beforehand) sizes and want to see where I can get by using this approach. It may allow me to optimize memory-layout for matrices (e.g. align row-vectors on 32-byte boundaries) and do other sorts of开发者_开发知识库 funky stuff. I expect to shoot myself in the foot an awful lot of times by doing this, but the main thing I'm trying to get here is experience and find out what works and what doesn't (and what is hard to do and what isn't).
@Bo Perrson : I know why the first version doesn't compile, but I was wondering if there was a simpler version of my second try that could work. Main problem is that MyMatrix is a class template itself and I need to get the template arguments of it somehow to the Transpose-struct.
@VJo : I don' t think that would work. If T is a MyMatrix<..> itself then the transpose matrix should have Transpose<T>
as a datatype, not T itself. For all basic types (char, int, double...), this is of course correct and way simpler.
Yes, you are complicating.
If you have declaration like this :
template< typename TDataType, size_t rows, size_t cols > class MyMatrix
then the transpose function should be like this :
template< typename T, size_t rows, size_t cols >
MyMatrix< T, cols, rows > Transpose( const MyMatrix< T, rows, cols > & m )
{
MyMatrix< T, cols, rows > res;
// implementation
return res;
}
What do you gain from making the rows / cols part of the template definition?
My personal feeling is that this is making things way too complex. By all means include the data type in the template, but including the dimensions seems wrong.
The first attempt fails because the specialization is a separate type and the template parameters of the base template are not known there.
The second verision is correct according to the language, but like Will A says - do you really want every combination of row and col to create a new type?
I would write it something like this, to allow arbitrary depths of recursion (matrices of matrices of matrices...)
template<typename T, unsigned rows, unsigned cols>
struct MyMatrix
{
typedef T value_type;
T stuff[rows][cols]; // or whatever
};
// For basic types, transpose is identity.
template<typename T>
struct Transpose {
typedef T result_type;
result_type operator()(const T & in) {
return in;
}
};
// For more complex types, specialize and invoke recursively.
template<typename T, unsigned rows, unsigned cols>
struct Transpose<MyMatrix<T, rows, cols> > {
typedef MyMatrix<Transpose<T>, cols, rows> result_type;
result_type operator()(const MyMatrix<T, rows, cols> & in) {
Transpose<T> transposer;
// (invoke transposer on each element of in and build result)
}
};
Here, Transpose is a functor; you create an instance of it but call it as a function. For extra credit, you could have it inherit from unary_function
and get the result_type
typedef for free...
精彩评论