шаблонный класс Matrix со статическим или динамическим размером

Я начал реализовывать следующий класс Matrix, чтобы лучше понимать классы шаблонов в целом. На данный момент ему не хватает многих функций, он выполняет очень простые вещи:

  • добавление
  • умножение (здесь доступно только умножение матрицы X)
  • транспонировать
  • преобразование в строку

Вот некоторые моменты, которые, на мой взгляд, могут быть интересными

  • Размеры матриц могут быть «статическими» или «динамическими». Под статическим я подразумеваю, что количество строк / столбцов не может изменяться, даже если данные хранятся в std :: vector

  • При сложении двух матриц размер результирующей матрицы является статическим, если хотя бы один из двух операндов является статическим.

  • При умножении двух матриц вместе размер результирующей матрицы зависит от строк в левой части и столбцов в правой части символа «*».

  • Тип данных, содержащихся в возвращаемой матрице, соответствует неявному приведению типов левой и правой частей выражения. Если пользователь решил умножить матрицу Foos, с матрицей Бары, это на нем.

  • Метод changeSize при вызове статической матрицы срабатывает только в том случае, если запрошенное изменение отличается от размера матрицы.

  • Я пока не беспокоился о типе исключения, которое использовал, поскольку это скорее упражнение, чем то, что я буду использовать в реальном приложении.

  • Причина, по которой я сделал возможным создание статических или динамических матриц, заключается в том, что я хочу, чтобы они могли управлять операциями Vector X Matrix. Поскольку вектор — это просто частный случай матриц, я решил объединить их в один класс. (в стиле Eigen, на который мой код вдохновлен с точки зрения используемого интерфейса).

Правильно ли я использую ссылки rvalues ​​и семантику перемещения? (об этом я узнал на прошлой неделе, любой совет может помочь)

Как вы думаете, что можно улучшить в моем коде?

Есть ли какое-то понятие, которое я должен изучить, прежде чем продолжить?

Были ли какие-либо варианты дизайна с точки зрения параметров шаблона хорошими или плохими?

#ifndef UTILS_MATRIX_H_
#define UTILS_MATRIX_H_

#include <vector>
#include <stdexcept>
#include <sstream>
#include <iomanip>

namespace mat
{

  constexpr size_t DYNAMIC = static_cast<size_t>(-1); // [-] No matrix can be of this size... right?

  template<typename TYPE, size_t NBROWS, size_t NBCOLS>
  class Matrix
  {
    private:

      std::vector<std::vector<TYPE>> matrixData;             // [-] Contents of the Matrix as a 2D array
      static constexpr bool isRowStatic = NBROWS != DYNAMIC; // [-] Is the number of rows of this matrix editable at run time
      static constexpr bool isColStatic = NBCOLS != DYNAMIC; // [-] Is the number of columns of this matrix editable at run time

    public:

      /** @brief Default constructor
       *
       *  @return Void.
       */
      Matrix() :
          matrixData()
      {
        if ((NBROWS <= 0 and NBROWS != DYNAMIC) or (NBCOLS <= 0 and NBCOLS != DYNAMIC))
          throw std::runtime_error("The number of rows and columns of a static matrix must be positive integers");

        // In case of a dynamic shape, the matrix should not be instanciated
        if(isRowStatic and isColStatic)
          internalChangeSize(NBROWS, NBCOLS);
      }

      /** @brief Consutuctor for the static size matrix
       *  @param i_DefaultValue IN : Default value used to fill
       *         the matrix
       *
       *  @return Void.
       */
      Matrix(const TYPE &i_DefaultValue) :
          matrixData()
      {
        // error handling
        if (not isRowStatic or not isColStatic)
          throw std::runtime_error("The default value constructor can not be called on a Matrix with at least one dynamic component");

        if ((NBROWS <= 0 and NBROWS != DYNAMIC) or (NBCOLS <= 0 and NBCOLS != DYNAMIC))
          throw std::runtime_error("The number of rows and columns of a static matrix must be positive integers");

        internalChangeSize(NBROWS, NBCOLS, i_DefaultValue);
      }

      /** @brief Consutuctor for the dynamic size matrix without default value
       *
       *  @param i_Nbrows       IN : Number of rows of the matrix
       *  @param i_Nbcols       IN : Number of columns of the matrix
       *
       *  @return Void.
       */
      Matrix(const size_t i_Nbrows,
             const size_t i_Nbcols) :
          matrixData()
      {
        // error handling
        if (i_Nbrows <= 0 or i_Nbcols <= 0)
          throw std::runtime_error("The number or rows and columns has to be a positive integer");

        // If one dimension is static, ignore the related constructor parameters
        size_t NbRows = i_Nbrows;
        size_t NbCols = i_Nbcols;
        if(isRowStatic)
          NbRows = NBROWS;
        if(isColStatic)
          NbCols = NBCOLS;

        internalChangeSize(NbRows, NbCols, TYPE());
      }

      /** @brief Consutuctor for the dynamic size matrix with default value
       *
       *  @param i_Nbrows       IN : Number of rows of the matrix
       *  @param i_Nbcols       IN : Number of columns of the matrix
       *  @param i_DefaultValue IN : Default value used to fill the
       *                             matrix
       *
       *  @return Void.
       */
      Matrix(const size_t i_Nbrows,
             const size_t i_Nbcols,
             const TYPE &i_DefaultValue) :
          matrixData()
      {
        // error handling
        if (i_Nbrows <= 0 or i_Nbcols <= 0)
          throw std::runtime_error("The number or rows and columns has to be a positive integer");

        // If one dimension is static, ignore the related constructor parameters
        size_t NbRows = i_Nbrows;
        size_t NbCols = i_Nbcols;
        if(isRowStatic)
          NbRows = NBROWS;
        if(isColStatic)
          NbCols = NBCOLS;

        internalChangeSize(NbRows, NbCols, i_DefaultValue);
      }

      /** @brief Copy constructor
       *
       *  @param i_otherMatrix IN : Matrix to be copied
       *
       *  @return Void.
       */
      Matrix(const Matrix<TYPE, NBROWS, NBCOLS>& i_otherMatrix):
          matrixData()
      {
        if(not i_otherMatrix.isEmpty())
          {
          changeSize(i_otherMatrix.getNbRows(), i_otherMatrix.getNbCols());
          matrixData = i_otherMatrix.matrixData;
          }
      }

      /** @brief Move constructor
       *
       *  @param i_otherMatrix IN : Matrix to be moved
       *
       *  @return Void.
       */
      Matrix(Matrix<TYPE, NBROWS, NBCOLS>&& i_otherMatrix):
          matrixData()
      {
        matrixData = std::move(i_otherMatrix.matrixData);
      }

      /** @brief getter for the matrix data vector
       *
       *  @return std::vector<std::vector<TYPE>>&.
       */
      const std::vector<std::vector<TYPE>>& getMatrixData() const
      {
        return matrixData;
      }

      /** @brief getter for the number or rows
       *
       *  @return size_t
       */
      size_t getNbRows() const
      {
        return matrixData.size();
      }

      /** @brief getter for the number or columns
        *
        *  @return size_t
        */
      size_t getNbCols() const
      {
        if(matrixData.size() > 0)
          return matrixData[0].size();
        else
          return 0;
      }

      /** @brief is the Matrix is empty
        *
        *  @return bool
        */
      bool isEmpty() const
      {
        return getNbRows() == 0 or getNbCols() == 0;
      }

      /** @brief function used to retrieve the shape of the matrix
       *  @param o_NbRows OUT : Number of rows of the matrix
       *  @param o_NbCols OUT : Number of columns of the matrix
       *
       *  @return Void.
       */
      std::pair<size_t, size_t> shape() const
      {
        return std::pair<size_t, size_t>(getNbRows(), getNbCols());
      }

      /** @brief function used to print the contents of the
       *         matrix formatted into a string
       *
       *  @return std::string
       */
      std::string toString(int i_Precision = 10) const
      {
        std::ostringstream SS;

        // if 0 lines representation is an empty bracket
        if(matrixData.size() == 0)
          SS << "| |" << std::endl;

        // This will align the floating point numbers
        SS << std::showpoint;
        SS << std::setprecision(i_Precision);
        for(auto& Row : matrixData)
        {
          SS << "|";
          for(auto& Value : Row)
          {
            SS << " " << Value << " ";
          }
          SS << "|" << std::endl;
        }
        return SS.str();
      }

      /** @brief function used to change the size of the matrix
       *         This method does not require a default value,
       *         hence it is default initialized
       *
       *  @param i_NbRows IN : New number of rows of the matrix
       *  @param o_NbCols IN : New number of columns of the matrix
       *
       *  @return Void.
       */
      void changeSize(const size_t i_NbRows, const size_t i_NbCols)
      {
        // Error handling
        if (i_NbRows <= 0 or i_NbCols <= 0)
          throw std::runtime_error("The number or rows and columns has to be a positive integer");

        if (isRowStatic and NBROWS != i_NbRows)
          throw std::runtime_error("You cannot change the number of rows, the matrix row size is static.");

        if(isColStatic and NBCOLS != i_NbCols)
          throw std::runtime_error("You cannot change the number of columns, the matrix columns size is static.");

        internalChangeSize(i_NbRows, i_NbCols);
      }

      /** @brief function used to change the size of the matrix
       *         This method does not require a default value,
       *         hence it is default initialized
       *
       *  @param i_NewShape IN : New shape of the matrix
       *
       *  @return Void.
       */
      void changeSize(const std::pair<size_t, size_t>& i_NewShape)
      {
        changeSize(i_NewShape.first, i_NewShape.second);
      }

      /** @brief function used to change the size of the matrix
       *         This method requires a default value for filling
       *         any new row / column.
       *
       *  @param i_NbRows     IN : New number of rows of the matrix
       *  @param o_NbCols     IN : New number of columns of the matrix
       *  @param DefaultValue IN : Default value used to fill the matrix
       *
       *  @return Void.
       */
      void changeSize(const size_t i_NbRows, const size_t i_NbCols, const TYPE &DefaultValue)
      {
        // error handling
        if (i_NbRows <= 0 or i_NbCols <= 0)
          throw std::runtime_error("The number or rows and columns has to be a positive integer");

        if (isRowStatic and NBROWS != i_NbRows)
          throw std::runtime_error("You cannot change the number of rows, the matrix columns size is static.");

        if(isColStatic and NBCOLS != i_NbCols)
          throw std::runtime_error("You cannot change the number of columns, the matrix columns size is static.");

        internalChangeSize(i_NbRows, i_NbCols, DefaultValue);
      }

      /** @brief function used to change the size of the matrix
       *         This method requires a default value for filling
       *         any new row / column.
       *
       *  @param i_NewShape   IN : New shape of the matrix
       *  @param DefaultValue IN : Default value used to fill the matrix
       *
       *  @return Void.
       */
      void changeSize(const std::pair<size_t, size_t>& i_NewShape, const TYPE &DefaultValue)
      {

        changeSize(i_NewShape.first, i_NewShape.second, DefaultValue);
      }

      /** @brief function used to transpose the current matrix.
       *         If the matrix is dynamically allocated, it's shape
       *         might be changed by the function
       *
       *  @return Void.
       */
      void transpose()
      {
        // Error handlingmatrixData
        if (isEmpty())
          throw std::runtime_error("The transpose function can not be called on an empty matrix");

        if ((isRowStatic or isColStatic) and getNbRows() != getNbCols())
          throw std::runtime_error("The transpose function can not be called on a non square matrix with at least one static component");

        // The transposed Matrix is built and replaces the old data
        std::vector<std::vector<TYPE>> newData(getNbCols(), std::vector<TYPE>(getNbRows(), (*this)(0,0)));

        for (size_t i = 0 ; i < getNbCols() ; i += 1)
          for (size_t j = 0 ; j < getNbRows() ; j += 1)
            newData[i][j] = (*this)(i,j);

        matrixData = std::move(newData);
      }

      /** @brief () operator. returns a reference to
       *         the value at row i, column j
       *
       *  @return const TYPE&
       */
      const TYPE& operator()(size_t i_Row, size_t i_Col) const
      {
        try
        {
          return matrixData.at(i_Row).at(i_Col);
        }
        catch(std::out_of_range& e)
        {
          const auto MatrixShape = this->shape();
          std::ostringstream SS;
          SS << "Indexes : (" << i_Row << ", " << i_Col << ") are out of bounds of the Matrix : " << "(" << MatrixShape.first << ", " << MatrixShape.second << ")";
          throw std::runtime_error(SS.str());
        }
      }

      /** @brief () operator. returns a reference to
       *         the value at row i, column j
       *
       *  @return const TYPE&
       */
      TYPE& operator()(size_t i_Row, size_t i_Col)
      {
        try
        {
          return matrixData.at(i_Row).at(i_Col);
        }
        catch(std::out_of_range& e)
        {
          const auto MatrixShape = this->shape();
          std::ostringstream SS;
          SS << "Indexes : (" << i_Row << ", " << i_Col << ") are out of bounds of the Matrix : " << "(" << MatrixShape.first << ", " << MatrixShape.second << ")";
          throw std::runtime_error(SS.str());
        }
      }

      /** @brief = operator. It copies the right hand side
       *         into the left hand size Matrix. If the sizes are
       *         different and the left hand side is static, the copy
       *         fails
       *  @param rhs  IN : Right hand side matrix
       *
       *  @return Void.
       */
      template<typename RHSTYPE, size_t RHSNBROWS, size_t RHSNBCOLS>
      Matrix<TYPE, NBROWS, NBCOLS>& operator=(const Matrix<RHSTYPE, RHSNBROWS, RHSNBCOLS> &rhs)
      {
        const auto LhsShape = this->shape();
        const auto RhsShape = rhs.shape();

        // Error handling
        if ((isRowStatic and (this->getNbRows() != rhs.getNbRows())) or
            (isColStatic and (this->getNbCols() != rhs.getNbCols())))
        {
          std::ostringstream SS;
          SS << "Impossible to fit data from a matrix of size (" << rhs.getNbRows() << ", " << rhs.getNbCols() << ") into"
                " a static matrix of size (" << this->getNbRows() << ", " << this->getNbCols() << ")";
          throw std::runtime_error(SS.str());
        }

        // If both matrices are empty, we dont need to do anything
        if(not(isEmpty() and rhs.isEmpty()))
        {
          // else, change the size only if necessary, taking a default value in one of the non empty matrices in order not to call the default constructor
          if (LhsShape != RhsShape )
          {
            if(not isEmpty())
            {
              changeSize(RhsShape, (*this)(0,0));
            }
            else if(not rhs.isEmpty())
            {
              changeSize(RhsShape, rhs(0,0));
            }
          }
        }

        matrixData = rhs.getMatrixData();
        return *this;
      }

      /** @brief move = operator. It moves the right hand side
       *         into the left hand size Matrix. If the sizes are
       *         different and the left hand side is static, the copy
       *         fails
       *  @param rhs  IN : Right hand side matrix
       *
       *  @return Void.
       */
      template<typename RHSTYPE, size_t RHSNBROWS, size_t RHSNBCOLS>
      Matrix<TYPE, NBROWS, NBCOLS>& operator=(Matrix<RHSTYPE, RHSNBROWS, RHSNBCOLS>&& rhs)
      {
        const auto LhsShape = this->shape();
        const auto RhsShape = rhs.shape();

        // Error handling
        if ((isRowStatic and (this->getNbRows() != rhs.getNbRows())) or
            (isColStatic and (this->getNbCols() != rhs.getNbCols())))
        {
          std::ostringstream SS;
          SS << "Impossible to fit data from a matrix of size (" << rhs.getNbRows() << ", " << rhs.getNbCols() << ") into"
                " a static matrix of size (" << this->getNbRows() << ", " << this->getNbCols() << ")";
          throw std::runtime_error(SS.str());
        }

        // If both matrices are empty, we dont need to resize anything
        if(not(isEmpty() and rhs.isEmpty()))
        {
          // else, change the size only if necessary, taking a default value in one of the non empty matrices in order not to call the default constructor
          if (LhsShape != RhsShape )
          {
            if(not isEmpty())
            {
              changeSize(RhsShape, (*this)(0,0));
            }
            else if(not rhs.isEmpty())
            {
              changeSize(RhsShape, rhs(0,0));
            }
          }
        }

        matrixData = std::move(rhs.matrixData);
        return *this;
      }

      /** @brief += operator. It adds the right hand side
       *         into the left hand size Matrix. If the sizes are
       *         different the operator fails
       *
       *  @param i_NbRows  IN : New number of rows of the matrix
       *  @param o_NbCols  IN : New number of columns of the matrix
       *  @param io_Matrix IN : Matrix which size will be reduced
       *
       *  @return Void.
       */
      template<typename RHSTYPE, size_t RHSNBROWS, size_t RHSNBCOLS>
      Matrix<TYPE, NBROWS, NBCOLS>& operator+=(const Matrix<RHSTYPE, RHSNBROWS, RHSNBCOLS> &rhs)
      {
        // Error handling
        if(this->isEmpty() or rhs.isEmpty())
          throw std::runtime_error("Adding empty matrices is forbidden");

        if (rhs.shape() != this->shape())
          throw std::runtime_error("Adding matrices of different shapes is forbidden");

        for(size_t i = 0; i < getNbRows()  ; i += 1)
          for(size_t j = 0 ; j < getNbCols() ; j += 1)
            matrixData[i][j] += rhs(i,j);

        return *this;
      }


      /** @brief + operator. It adds both sides of the "+" sign
       *         and returns a static size matrix. Both sides
       *         have to be of the same shape, otherwise the
       *         operator fails.
       *
       *  @param rhs IN : Matrix, right side of the equation
       *
       *  @return Matrix<decltype(rhs(0,0) + (*this)(0,0)), NBROWS, NBCOLS, true>.
       */
      template<typename LHSTYPE, size_t LHSNBROWS, size_t LHSNBCOLS, typename RHSTYPE, size_t RHSNBROWS, size_t RHSNBCOLS>
      friend auto operator+(const Matrix<LHSTYPE, LHSNBROWS, LHSNBCOLS> &lhs, const Matrix<RHSTYPE, RHSNBROWS, RHSNBCOLS> &rhs) ->
             Matrix<decltype(lhs(0,0) + rhs(0,0)), LHSNBROWS == DYNAMIC ? RHSNBROWS : DYNAMIC,
                                                   LHSNBCOLS == DYNAMIC ? RHSNBCOLS : DYNAMIC>;

      /** @brief operator* It multiplies both sides of the "*" sign
       *         and returns a static size matrix. Both sides
       *         have to have compatible sizes, (lhs.columns == rhs.cols)
       *         otherwise, the operator fails.
       *
       *  @param rhs IN : Matrix, right side of the equation
       *
       *  @return Matrix<decltype(rhs(0,0) * (*this)(0,0)), this->getNbRows(), rhs.getNbCols(), true>
       */
      template<typename LHSTYPE, size_t LHSNBROWS, size_t LHSNBCOLS, typename RHSTYPE, size_t RHSNBROWS, size_t RHSNBCOLS>
      friend auto operator*(const Matrix<LHSTYPE, LHSNBROWS, LHSNBCOLS> &lhs, const Matrix<RHSTYPE, RHSNBROWS, RHSNBCOLS> &rhs) -> Matrix<decltype(rhs(0,0) * lhs(0,0)), LHSNBROWS, RHSNBCOLS>;


    private:
      /** @brief function used to change the size of the matrix
       *         This method does not require a default value,
       *         hence it is default initialized. It does not check if the matrix is static or not
       *
       *  @param i_NbRows IN : New number of rows of the matrix
       *  @param o_NbCols IN : New number of columns of the matrix
       *
       *  @return Void.
       */
      void internalChangeSize(const size_t i_NbRows, const size_t i_NbCols)
      {
        // Error handling
        if (i_NbRows <= 0 or i_NbCols <= 0)
          throw std::runtime_error("The number or rows and columns should be a positive integer");

        matrixData.resize(i_NbRows, std::vector<TYPE>(i_NbCols, TYPE()));
        for (auto& row : matrixData)
          row.resize(i_NbCols, TYPE());
      }

      /** @brief function used to change the size of the matrix
       *         This method requires a default value for filling
       *         any new row / column. It does not check if the matrix is static or not
       *
       *  @param i_NbRows     IN : New number of rows of the matrix
       *  @param o_NbCols     IN : New number of columns of the matrix
       *  @param DefaultValue IN : Default value used to fill the matrix
       *
       *  @return Void.
       */
      void internalChangeSize(const size_t i_NbRows, const size_t i_NbCols, const TYPE &DefaultValue)
      {
        // error handling
        if (i_NbRows <= 0 or i_NbCols <= 0)
          throw std::runtime_error("The number or rows and columns has to be a positive integer");

        matrixData.resize(i_NbRows, std::vector<TYPE>(i_NbCols, DefaultValue));
        for (auto& row : matrixData)
          row.resize(i_NbCols, DefaultValue);
      }
    };

  /** @brief + operator. It adds both sides of the "+" sign
   *         and returns a static size matrix. Both sides
   *         have to be of the same shape, otherwise the
   *         operator fails.
   *
   *  @param rhs IN : Matrix, right side of the equation
   *
   *  @return Matrix<decltype(rhs(0,0) + (*this)(0,0)), NBROWS, NBCOLS, true>.
   */
  template<typename LHSTYPE, size_t LHSNBROWS, size_t LHSNBCOLS, typename RHSTYPE, size_t RHSNBROWS, size_t RHSNBCOLS>
    auto operator+(const Matrix<LHSTYPE, LHSNBROWS, LHSNBCOLS> &lhs, const Matrix<RHSTYPE, RHSNBROWS, RHSNBCOLS> &rhs) ->
      Matrix<decltype(lhs(0,0) + rhs(0,0)), LHSNBROWS == DYNAMIC ? RHSNBROWS : DYNAMIC,
                                            LHSNBCOLS == DYNAMIC ? RHSNBCOLS : DYNAMIC>
    {
      // Error handling
      if(lhs.isEmpty() or rhs.isEmpty())
        throw std::runtime_error("Adding empty matrices is forbidden");

      if (rhs.shape() != lhs.shape())
        throw std::runtime_error("Adding matrices of different shapes is forbidden");

      Matrix<decltype(lhs(0,0) + rhs(0,0)), LHSNBROWS == DYNAMIC ? RHSNBROWS : DYNAMIC,
                                            LHSNBCOLS == DYNAMIC ? RHSNBCOLS : DYNAMIC> newMatrix(lhs.getNbRows(), lhs.getNbCols(), lhs(0,0));

      for(size_t i = 0 ; i < rhs.getNbRows() ; i += 1)
        for(size_t j = 0 ; j < rhs.getNbCols() ; j += 1)
          newMatrix(i, j) = lhs(i,j) + rhs(i,j);

      return newMatrix;
    }

  /** @brief operator* It multiplies both sides of the "*" sign
   *         and returns a static size matrix. Both sides
   *         have to have compatible sizes, (lhs.columns == rhs.cols)
   *         otherwise, the operator fails.
   *
   *  @param rhs IN : Matrix, right side of the equation
   *
   *  @return Matrix<decltype(rhs(0,0) * (*this)(0,0)), this->getNbRows(), rhs.getNbCols(), true>
   */
  template<typename LHSTYPE, size_t LHSNBROWS, size_t LHSNBCOLS, typename RHSTYPE, size_t RHSNBROWS, size_t RHSNBCOLS>
  auto operator*(const Matrix<LHSTYPE, LHSNBROWS, LHSNBCOLS> &lhs, const Matrix<RHSTYPE, RHSNBROWS, RHSNBCOLS> &rhs) -> Matrix<decltype(rhs(0,0) * lhs(0,0)), LHSNBROWS, RHSNBCOLS>
  {
    // error handling
    if(lhs.isEmpty() or rhs.isEmpty())
      throw std::runtime_error("Multiplying empty matrices is forbidden");

    if(lhs.getNbCols() != rhs.getNbRows())
      throw std::runtime_error("The size of the matrices is incompatible with matrix multiplication");

    Matrix<decltype(rhs(0,0) * lhs(0,0)), LHSNBROWS, RHSNBCOLS> newMatrix(lhs.getNbRows(), rhs.getNbCols(), lhs(0,0));

    for(size_t i = 0 ; i < lhs.getNbRows() ; i += 1)
    {
      for(size_t j = 0 ; j < rhs.getNbCols(); j += 1)
      {
        decltype(rhs(0,0) * lhs(0,0)) sum = lhs(i,0) * rhs(0,j); // Compute the first element of the sum in order not to call the default constructor
        for(size_t k = 1 ; k < lhs.getNbRows() ; k += 1)
        {
          sum += lhs(i,k) * rhs(k,j);
        }
        newMatrix(i, j) = std::move(sum);
      }
    }
    return newMatrix;
  }

  /** @brief operator<< outputs the Matrix into a stream
   *
   *  @param i_Matrix IN  : Matrix to output
   *  @param o_OS     OUT : Stream in which the matrix must be fitted
   *
   *  @return std::ostream&
   */
  template <typename TYPE, size_t NBROWS, size_t NBCOLS>
  std::ostream& operator<<(std::ostream& o_OS, const Matrix<TYPE, NBROWS, NBCOLS>& i_Matrix)
  {
      return o_OS << i_Matrix.toString();
  }
} /* namespace Mat */

[EDIT]

Как и просили, я написал эту главную, в которой я создаю несколько матриц, складываю и умножаю их. Некоторые статичны, некоторые — динамичны. Я не показал случай, когда только одно измерение матрицы является динамическим, а другое — статическим (например, в векторе динамического размера), но это работает одинаково.

Я использовал эти параметры при компиляции при работе с классом: -std = c ++ 11 -O0 -g -pedantic -pedantic-errors -Wall -Wextra -Werror -Wconversion -fmessage-length = 0 Однако для поведения неявного преобразования для работы в тестовой главной программе -Wconversion использовать нельзя.

#include <iostream>

#include "Matrix.h"

int main()
{
  // Test of static Matrix operations
  // ==============================================
  std::cout << "==============================================" << std::endl;
  std::cout << "===============STATIC MATRICES================" << std::endl;
  std::cout << "==============================================" << std::endl;
  mat::Matrix<int, 3,2> StaticMatrix_1(21);
  mat::Matrix<float, 3,2> StaticMatrix3x2(2.0);
  mat::Matrix<float, 3,3> StaticMatrix3x3(2.0);

  // Resizing a static matrix should throw if it changes the size of the matrix only
  // should not throw
  StaticMatrix_1.changeSize(3, 2);
  try
  {
    StaticMatrix_1.changeSize(4, 5);
    std::cout << "Did not throw ==> problem" << std::endl;
  }
  catch(std::runtime_error& E)
  {
  }

  std::cout << "STATIC MATRICES USED:" << std::endl;
  std::cout << StaticMatrix_1 << std::endl;
  std::cout << StaticMatrix3x2 << std::endl;
  std::cout << StaticMatrix3x3 << std::endl;

  // test of Matrix multiplication: Should yield a 3 by 2 matrix, full of 12.0 floats
  std::cout << "TEST_ADDITION FLOAT + FLOAT" << std::endl;
  std::cout << StaticMatrix3x3 * StaticMatrix3x2 << std::endl;

  // test of matrix addition: This should yield a matrix of floats as int + float does yield a float
  std::cout << "TEST_ADDITION INT + FLOAT" << std::endl;
  std::cout << StaticMatrix_1 + StaticMatrix3x2 << std::endl;

  // Test of matrix addition with different sizes (should throw)
  try
  {
    StaticMatrix3x2 + StaticMatrix3x3;
    std::cout << "Did not throw ==> problem" << std::endl;
  }
  catch(std::runtime_error& E)
  {
  }

  // This property should be commutative
  std::cout << "TEST_ADDITION FLOAT + INT" << std::endl;
  std::cout << StaticMatrix3x2 + StaticMatrix_1 << std::endl;

  std::cout << "TEST_MATRIX_MULTIPLICATION" << std::endl;
  std::cout << StaticMatrix3x3 * StaticMatrix3x2 << std::endl;
  // This should throw (incompatible sizes for multiplication)
  try
  {
    StaticMatrix3x2 * StaticMatrix3x3;
    std::cout << "Did not throw ==> problem" << std::endl;
  }
  catch(std::runtime_error& E)
  {
  }

  std::cout << std::endl << std::endl << std::endl;
  std::cout << "==============================================" << std::endl;
  std::cout << "===============DYNAMIC MATRICES===============" << std::endl;
  std::cout << "==============================================" << std::endl;

  // We use the same types, contents and sizes
  mat::Matrix<int,   mat::DYNAMIC, mat::DYNAMIC> DynamicMatrix_1(3, 2, 21);
  mat::Matrix<float, mat::DYNAMIC, mat::DYNAMIC> DynamicMatrix3x2(3, 2, 2.0);
  mat::Matrix<float, mat::DYNAMIC, mat::DYNAMIC> DynamicMatrix3x3(3, 3, 2.0);

  std::cout << "DYNAMYC MATRICES USED:" << std::endl;
  std::cout << DynamicMatrix_1 << std::endl;
  std::cout << DynamicMatrix3x2 << std::endl;
  std::cout << DynamicMatrix3x3 << std::endl;

  // Resizing a dynamic matrix should throw only if the requested size is <= 0
  // should not throw
  DynamicMatrix_1.changeSize(1, 1);
  DynamicMatrix_1.changeSize(3, 2);
  try
  {
    DynamicMatrix_1.changeSize(0, 0);
    std::cout << "Did not throw ==> problem" << std::endl;
  }
  catch(std::runtime_error& E)
  {
  }

  // test of Matrix multiplication: Should yield a 3 by 2 matrix, full of 12.0 floats
  std::cout << "TEST_ADDITION FLOAT + FLOAT" << std::endl;
  std::cout << DynamicMatrix3x3 * DynamicMatrix3x2 << std::endl;

  // test of matrix addition: This should yield a matrix of floats as int + float does yield a float
  std::cout << "TEST_ADDITION INT + FLOAT" << std::endl;
  std::cout << DynamicMatrix_1 + DynamicMatrix3x2 << std::endl;

  // Test of matrix addition with different sizes (should throw)
  try
  {
    DynamicMatrix3x2 + DynamicMatrix3x3;
    std::cout << "Did not throw ==> problem" << std::endl;
  }
  catch(std::runtime_error& E)
  {
  }

  // This property should be commutative
  std::cout << "TEST_ADDITION FLOAT + INT" << std::endl;
  std::cout << DynamicMatrix3x2 + DynamicMatrix_1 << std::endl;

  std::cout << "TEST_MATRIX_MULTIPLICATION" << std::endl;
  std::cout << DynamicMatrix3x3 * DynamicMatrix3x2 << std::endl;
  // This should throw (incompatible sizes for multiplication)
  try
  {
    DynamicMatrix3x2 * DynamicMatrix3x3;
    std::cout << "Did not throw ==> problem" << std::endl;
  }
  catch(std::runtime_error& E)
  {
  }

  return 0;
}

```

0

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *