Шаблоны С++ 11, определяющие тип возврата
Я создаю библиотеку матриц, и я пытаюсь использовать политический дизайн.
Поэтому мои базовые классы - это классы, которые предоставляют метод хранения, а некоторые
функции доступа.
У меня также есть функциональная матрица, которая обеспечивает математические функции.
Это отлично работает, но есть серьезная проблема с оператором *
из-за возвращаемого типа. Я объясню это с помощью некоторого кода.
Базовый класс, обеспечивающий хранилище стека:
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*
работает только для квадратной матрицы.
Поэтому нам все еще нужен один для всех матриц. И это было
неправильно. Я уже много пробовал, но ничего не работает.
Я ищу что-то подобное, с помощью С++ 0x (использование
С++ 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;
Здесь он будет жаловаться на тип, потому что он не соответствует ни одному из двух типов параметров. Но компилятор должен знать тип во время компиляции, и я не знаю, как его предоставить.
Я знаю, что есть другие варианты дизайна, но я действительно ищу решение для этого сценария.
Спасибо!
Ответы
Ответ 1
Поднимая идею @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;
Ответ 2
Как изменить 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;
}
Таким образом вы не только проверяете размеры времени компиляции, но также можете расширить это, чтобы разрешить умножения между матрицами разных типов контейнеров.
Ответ 3
Просто потому, что я работал над этим, прежде чем найти здесь все ответы:
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*
для вывода (и проверки) аргументов шаблона, что приводит к ошибке времени компиляции, если ограничения умножения матрицы не выполняются.
Ответ 4
просто случайная идея, что, если вы включите в свой базовый класс способ получить контейнер того же типа, но другого размера? что-то в строках:
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");
// ...
}
// ....
}
btw, я не уверен, что получил область MatrixContainer:: resize right...
my 2c
Ответ 5
В вашем посте, которое я прочитал, вы хотели бы использовать политический дизайн. В этом случае вам сначала нужно определить свои классы политики. Таким образом, вам сначала нужно решить, какие классы вы хотите, чтобы ваши пользователи могли предоставлять самостоятельно. Какие классы вы принимаете, зависит от вас. Вы можете использовать для экземпляров политики Storage and 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. Вам просто нужно перегрузить общую функцию шаблона. Преимущество использования политик заключается в том, что теперь ваши пользователи могут, например, легко предоставить свои собственные хранилища.
Одна последняя заметка. Поскольку вы можете использовать С++ 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>;