Шаблоны C ++ 11, определяющие тип возвращаемого значения - PullRequest
14 голосов
/ 27 мая 2011

Я создаю матричную библиотеку и пытаюсь использовать основанный на политике дизайн .Поэтому мои базовые классы - это классы, которые предоставляют метод хранения и некоторые функции доступа.У меня также есть матрица функций, которая обеспечивает математические функции.Это прекрасно работает, но есть серьезная проблема с оператором * из-за типа возвращаемого значения.Я объясню это с помощью некоторого кода.

Базовый класс, который обеспечивает хранилище стека:

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;

Здесь он будет жаловаться на тип, потому что он не соответствует ни одному из двух типов параметров.Но компилятор должен знать тип во время компиляции, и я не знаю, как его предоставить.

Я знаю, что есть другие варианты дизайна, но я действительно ищу решение для этого сценария..

Спасибо!

Ответы [ 5 ]

5 голосов
/ 27 мая 2011

Продолжая идею @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;
3 голосов
/ 27 мая 2011

Как насчет изменения 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 голосов
/ 27 мая 2011

Просто потому, что я работал над этим, прежде чем найти все ответы здесь:

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* для вывода (и проверки) аргументов шаблона, что приводит к ошибке времени компиляции, если ограничения умножения матрицы не выполняются.

0 голосов
/ 29 мая 2011

В вашем посте я прочитал, что вы хотели бы использовать основанный на политике дизайн.В этом случае вам сначала нужно определить классы вашей политики.Таким образом, сначала вам нужно решить, какие классы вы хотите, чтобы ваши пользователи могли предоставить самостоятельно.Какие уроки вы принимаете, решать только вам.Для экземпляров вы можете использовать политики 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;
}

Как видите, вы также можете легко добавить два разных типа 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>;
0 голосов
/ 27 мая 2011

просто случайная идея, что если вы включите в свой базовый класс способ получить контейнер того же типа, но другого размера? что-то вроде:

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 :: изменить размер вправо ...

мой 2с

...