14

マトリックス ライブラリを構築しており、ポリシー ベースの設計を使用しようとしています。したがって、私の基本クラスは、ストレージ メソッドといくつかのアクセス関数を提供するクラスです。また、数学関数を提供する関数マトリックスもあります。これはうまく機能しますが、戻り値の型が原因で operator* に大きな問題があります。いくつかのコードで説明します。

スタック ストレージを提供する基本クラス:

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*正方行列でのみ機能します。したがって、すべての行列に 1 つ必要です。そして、それはうまくいかなかった場合です。私はすでにいくつかのことを試しましたが、何も機能しません。私はこのようなものを探します。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;

ここでは、2 つのパラメーターの型のいずれにも一致しないため、型について文句を言います。しかし、コンパイラはコンパイル時に型を知る必要があり、それを提供する方法がわかりません。

設計には他のオプションがあることは知っていますが、このシナリオの解決策を本当に探しています..

ありがとうございました !

4

5 に答える 5

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;
于 2011-05-26T23:02:57.770 に答える
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;
}

このようにして、コンパイル時の次元チェックを取得できるだけでなく、これを拡張して、異なるコンテナー タイプのマトリックス間の乗算を許可することもできます。

于 2011-05-26T22:52:01.690 に答える
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*てテンプレート引数を推測 (およびチェック) するため、行列乗算の制約が満たされない場合、コンパイル時にエラーが発生します。

于 2011-05-27T00:52:26.777 に答える
0

あなたの投稿で、ポリシーベースの設計を使用したいと読んでいます。その場合、最初にポリシー クラスを定義する必要があります。したがって、最初に、ユーザーが独自に提供できるクラスを決定する必要があります。どのクラスを受講するかはあなた次第です。たとえば、ポリシー Storage および Shape を使用できます。

次のようなものを作ることができます

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;
}

ご覧のとおり、2 つの異なる 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>;
于 2011-05-28T21:08:23.200 に答える
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 のスコープが正しいかどうかはわかりません...

私の2c

于 2011-05-27T00:54:47.490 に答える