我正在构建一个矩阵库,我正在尝试使用policy-based design。 所以我的基类是提供存储方法的类和一些 访问功能。 我还有一个提供数学函数的函数矩阵。 这很好用,但操作员有一个主要问题* 因为返回类型。我将用一些代码解释它。
提供堆栈存储的基类:
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;
然后我有我的矩阵类,它提供数学功能:
template <typename MatrixContainer >
class matrix : public MatrixContainer {
public:
typedef MatrixContainer Mcontainer;
matrix<Mcontainer>& operator +(const matrix<Mcontainer>&);
matrix<Mcontainer>& operator *(const matrix<Mcontainer>&);
operator+
始终有效,operator*
仅适用于方阵。
所以我们仍然需要一个所有矩阵。就是这样
错误。我已经尝试了很少的东西,但没有尝试。
我在c ++ 0x的帮助下寻找类似的东西(使用
c ++ 0x不是必需的)
你应该注意到“???” :)
friend auto operator * (const matrix<T1>& matrix1, const matrix<T2>& matrix2)
-> decltype(matrix<???>);
问题的一个例子
matrix<denseStackMatrix<int,3,2> > matrix1;
matrix<denseStackMatrix<int,2,4> > matrix2;
matrix<denseStackMatrix<int,3,4> > matrix3 = matrix1 * matrix2;
这里会抱怨类型,因为它与两种参数类型中的任何一种都不匹配。但编译器需要在编译时知道类型,我不知道如何提供它。
我知道设计还有其他选择,但我真的在寻找这种情况的解决方案..
谢谢!
答案 0 :(得分:5)
了解@hammar的想法,但部分专业化允许正常的语法,如问题所示:
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;
// ...
}
};
编辑:正如您在评论中所说,您也可以使用它来创建一个矩阵,该矩阵不使用具有行和列大小作为模板参数的MatrixContainer:
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
}
};
用法:
// 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;
答案 1 :(得分:3)
如何将MatrixContainer
更改为模板模板参数?
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;
}
这样,您不仅可以获得编译时维度检查,还可以对此进行扩展,以允许在不同容器类型的矩阵之间进行乘法运算。
答案 2 :(得分:2)
因为我在找到所有答案之前就已经开始研究了这个问题:
template <typename T, unsigned int M, unsigned int N>
struct Matrix
{
};
template <typename T, unsigned int M, unsigned int MN, unsigned int N>
Matrix<T, M, N> operator*(Matrix<T, M, MN> const & lhs, Matrix<T, MN, N> const & rhs)
{
return Matrix<T, M, N>();
}
int main()
{
Matrix<int, 3, 4> prod = Matrix<int, 3, 2>() * Matrix<int, 2, 4>();
// Fails to compile as desired
// g++ gives:
//matrix.cpp: In function 'int main()':
//matrix.cpp:20: error: no match for 'operator*' in 'Matrix<int, 3u, 2u>() * Matrix<int, 3u, 4u>()'
Matrix<int, 3, 4> prod1 = Matrix<int, 3, 2>() * Matrix<int, 3, 4>();
}
此解决方案可能不适合您的设计模式,但使用operator*
的自由函数实现来推断(并检查)模板参数,如果不满足矩阵乘法的约束,则会导致编译时错误
答案 3 :(得分:0)
只是一个随机的想法,如果你在你的基类中包含一个获得相同类型但不同大小的容器的方法怎么办?有些东西:
template<typename T, unsigned int Rows, unsigned int Cols>
class denseStackMatrix {
public:
static const int rows = Rows;
static const int cols = Cols;
template<unsigned int R, unsigned int C>
struct resize {
typedef denseStackMatrix<T, R, C> type;
};
// ....
}
然后你可以做
template <typename MatrixContainer >
class matrix : public MatrixContainer {
using MatrixContainer::resize;
public:
template<typename RHSMcontainer>
matrix<typename resize<rows, RHSMcontainer::cols>::type>
operator *(const matrix<RHSMcontainer>&)
{
static_assert(cols == RHSMcontainer::rows, "incompatible sizes");
// ...
}
// ....
}
顺便说一句,我不确定我对MatrixContainer :: resize的范围是否正确......
my 2c
答案 4 :(得分:0)
在我发布的帖子中,您希望使用基于策略的设计。在这种情况下,您首先需要定义策略类。因此,您首先需要确定您希望用户可以自己提供哪些类。你选哪些课程取决于你。您可以在实例中使用策略存储和形状。
您可以制作类似
的内容class Diagonal {
public:
// the default storage facility of a Diagonal matrix
typedef Stack default_storage;
};
template <typename T, typename Shape = Dense, typename Storage = Stack, unsigned Row, unsigned Col>
class Matrix : public Storage, Shape { // policy classes Storage and Shape
public:
template <typename T1, typename Shape1, typename Storage1, unsigned Row1, unsigned Col1>
friend Matrix<T1,Shape1,Storage1,Row1,Col1>& operator += (Matrix<T1,Shape1,Storage1,Row1,Col1> matrix1,Matrix<T,Shape,Storage,Row,Col> matrix2);
template <typename T1, typename Shape1, typename Storage1, unsigned Row1, unsigned Col1>
friend Matrix<T1,Diagonal,Storage1,Row1,Col1>& operator += (Matrix<T1,Diagonal,Storage1,Row1,Col1> matrix1,Matrix<T,Diagonal,Storage,Row,Col> matrix2);
template <typename T1, typename Shape1, typename Storage1, unsigned Row1, unsigned Col1>
friend Matrix<T,Shape,Storage,Row,Col>& operator + (Matrix<T,Shape,Storage,Row,Col> matrix1, Matrix<T,Shape,Storage,Row,Col> matrix2);
template <typename T1, typename Shape1, typename Storage1, unsigned Row1, unsigned Col1>
friend Matrix<T,Diagonal,Storage,Row,Col>& operator + (Matrix<T,Diagonal,Storage,Row,Col> matrix1, Matrix<T,Diagonal,Storage,Row,Col> matrix2);
// general template function
template <typename T, typename Shape, typename Storage, unsigned Row, unsigned Col>
Matrix<T,Shape,Storage,Row,Col>& operator + (Matrix<T,Shape,Storage,Row,Col> matrix1, Matrix<T,Shape,Storage,Row,Col> matrix2) {
Matrix<T,Shape,Storage,Row,Col>* result = new Matrix<T,Shape,Storage,Row,Col>();
for (unsigned i = 0; i < matrix1.getRowSize(); i++) { // getRowSize is a member function of policy class Storage
for (unsigned j = 0; j < matrix1.getRowSize(); j++) {
(*result)(i,j) = matrix1.getValue(i,j) + matrix2.getValue(i,j);
}
}
return *result;
}
// overloaded template function
template <typename T, typename Shape, typename Storage, unsigned Row, unsigned Col>
Matrix<T,Diagonal,Storage,Row,Col>& operator + (Matrix<T,Diagonal,Storage,Row,Col> matrix1, Matrix<T,Diagonal,Storage,Row,Col> matrix2) {
Matrix<T,Shape,Storage,Row,Col>* result = new Matrix<T,Shape,Storage,Row,Col>();
for (unsigned i = 0; i < matrix1.getRowSize(); i++) {
(*result)(i,i) = matrix1.getValue(i,i) + matrix2.getValue(i,i);
}
return *result;
}
// general template function
template <typename T, typename Shape, typename Storage, unsigned Row, unsigned Col>
Matrix<T,Shape,Storage,Row,Col>& operator += (Matrix<T,Shape,Storage,Row,Col> matrix1,Matrix<T,Shape,Storage,Row,Col> matrix2) {
Matrix<T,Shape,Storage,Row,Col>* result = new Matrix<T,Shape,Storage,Row,Col>(matrix1); // copy constructor
for (unsigned i = 0; i < matrix1.getRowSize(); i++) {
for (unsigned j = 0; j < matrix1.getRowSize(); j++) {
(*result)(i,j) = matrix1.getValue(i,j) + matrix2.getValue(i,j);
}
}
return *result;
}
// overloaded template function
template <typename T, typename Shape, typename Storage, unsigned Row, unsigned Col>
Matrix<T,Diagonal,Storage,Row,Col>& operator += (Matrix<T,Diagonal,Storage,Row,Col> matrix1,Matrix<T,Diagonal,Storage,Row,Col> matrix2) {
Matrix<T,Shape,Storage,Row,Col>* result = new Matrix<T,Shape,Storage,Row,Col>(matrix1); // copy constructor
for (unsigned i = 0; i < matrix1.getRowSize(); i++) {
(*result)(i,i) = matrix1.getValue(i,i) + matrix2.getValue(i,i);
}
return *result;
}
如您所见,您现在还可以轻松添加两种不同的Matrix类型。您只需要重载一般模板功能。使用策略的一个优点是,您的用户现在可以轻松地提供自己的存储设施。
最后一点。由于您可以使用C ++ 0x,您还可以为您的用户制作一些快捷方式。你可以做一些像
这样的事情template<typename T, unsigned Row, unsigned Col>
using DenseStackMatrix = Matrix<T, Dense, Stack, Row, Col>;
template<typename T>
using DenseHeapMatrix = Matrix<T, Dense, Heap, 0, 0>;