C ++ - Обеспечение цепочки перегрузки оператора - PullRequest
0 голосов
/ 01 марта 2020

Поэтому я пытаюсь перегрузить два оператора для своего класса "Матрица" (+ и + =). Я пытаюсь сделать + цепным и += нецепным:

template <class T>
Matrix<T>& Matrix<T>::operator+=(const Matrix& M) 
{
  if (this->m_capacity != M.capacity()) 
  {
    throw std::out_of_range("Input is invalid");
  }

  for (unsigned int i = 0; i < M.rows(); i++) 
  {
    for (unsigned int j = 0; j < M.cols(); j++) 
    {
      this->m_vec[i + m_cols * j] += M(i, j);
    }
  }
  return *this;
}


template <class T>
Matrix<T> operator+(Matrix<T> M1, Matrix<T>& M2) 
{
  if (M1.capacity() != M2.capacity()) 
  {
    throw std::out_of_range("Input is invalid");
  }

  return M1 += M2;
}

Он компилируется просто, проблем нет, но когда я пытаюсь выполнить юнит-тест на этом, весь тест программа просто падает при попытке связать оператор +.

Пример:

TEST(add, Matrix)
{
  Matrix<int> M1 = Matrix<int>(2, 3);
  Matrix<int> M2 = { 1, 2, 3, 4 };
  Matrix<int> M3 = M2;
  Matrix<int> M4 = { 2, 4, 6, 8 };

  ASSERT_THROW(M1 + M2, std::out_of_range);
  ASSERT_EQ((M2 + M3) == M4, true);

  M2 += M3;
  M2 = M4 + M4 + M4; // As soon as this line is added, it crashes, without it, test works fine

  ASSERT_EQ(M2 == M4, true);
}

Есть идеи, почему происходит сбой? Как я могу переписать перегрузки моего оператора так, чтобы ´ + ´ было цепным (а += нет)?

РЕДАКТИРОВАТЬ: Вот мой оператор = (по запросу)

template <class T>
void Matrix<T>::operator=(Matrix & M){
  T*temp = new T[M.m_capacity];
  for(unsigned int  j = 0; j < M.m_capacity; j++){
    temp[j] = M.m_vec[j];
  }
  delete[] this -> m_vec;
  size_t rows = M.get_m_rows();
  size_t cols = M.get_m_cols();
  this -> m_rows = rows;
  this -> m_cols = cols;
  this -> m_vec = new T [rows*cols];
  this -> m_capacity = rows*cols;
  for(size_t i = 0;i < rows;i++){
    for(size_t j = 0;j < cols;j++){
      this -> m_vec[i*cols+j] = temp[i*cols +j];
    }
  }
  delete [] temp;
}

EDIT2: добавлен дополнительный контекст (по запросу, заголовок, конструкторы и т. Д. c.)

Заголовок:

template <class T>
class Matrix {
public:
   // constructor
   Matrix(unsigned int n);
   Matrix(unsigned int n, unsigned int m);
   Matrix();
   Matrix(const T n);
   Matrix(Matrix &obj);
   ~Matrix();
   Matrix(Matrix &&obj);
   Matrix(std::initializer_list<T> l);

   // operators
   void operator=(Matrix & obj);
   T& operator()(unsigned int row, unsigned int col);
   Matrix& operator=( Matrix &&obj);
   Matrix& operator+=(const Matrix& M)
   void operator+=(const T number);
   void operator-=(const T number);
   void operator-=(Matrix &obj);
   void operator*=(const T number);
   void operator*=(Matrix &obj);
   bool operator==(Matrix & rhs);

private:
   std::size_t m_rows;
   std::size_t m_cols;
   std::size_t m_capacity;
   T * m_vec;
};

Конструктор копирования:

template <class T>
Matrix<T>::Matrix(Matrix &obj){
  size_t rows = obj.get_m_rows();
  size_t cols = obj.get_m_cols();
  this -> m_rows = rows;
  this -> m_cols = cols;
  this -> m_vec = new T [rows*cols];
  this -> m_capacity = rows*cols;
  for(size_t i = 0;i < rows;i++){
    for(size_t j = 0;j < cols;j++){
      this -> m_vec[i*cols+j] = obj(i,j);
    }
  }
}

Деструктор :

template  <class T>
Matrix<T>::~Matrix(){
  delete [] m_vec;
}

Переместить конструктор (возможно, не работает)

template <class T>
Matrix<T>::Matrix(Matrix &&obj){
  size_t rows = obj.get_m_rows();
  size_t cols = obj.get_m_cols();
  this -> m_rows = rows;
  this -> m_cols = cols;
  this -> m_vec = new T [rows*cols];
  this -> m_capacity = rows*cols;
  m_vec = nullptr;
}

Переместить назначение (возможно, не работает)

template <class T>
Matrix<T>& Matrix<T>::operator=(Matrix &&obj){
  if (this !=&obj)
  {
    delete [] m_vec;
    obj.m_rows = 0;
    obj.m_cols = 0;
    obj.m_capacity = 0;
    obj.m_vec = nullptr;
  }
  return *this;
  }

Ответы [ 2 ]

2 голосов
/ 01 марта 2020

И ваш конструктор перемещения, и ваш оператор присваивания перемещения не реализуют правильную семантику (и, кажется, вам это известно), что приводит к UB где-то позже. (Я не удосужился проверить, где именно.)

Я полагаю, вы предполагали, что вы на самом деле не вызываете эти операторы, но это неправильно.

Вы вызываете оператор присваивания перемещения на знак =

M2 = M4 + M4 + M4;

, потому что правая часть является prvalue (operator+ возвращает значение-по-умолчанию), который может связываться со ссылкой на rvalue.

(Before C ++ 17) Вы вызываете (возможно, исключенный) конструктор перемещения во втором + в той же строке, чтобы создать первый параметр operator+, поскольку первый + приводит к значению prvalue.

Если вы намереваетесь реализовать операции перемещения позже и у вас все в порядке с использованием вместо этого реализаций копирования, то вообще не объявляйте операции перемещения в своем классе. Тогда компилятор выберет ваши реализации копирования.

Кроме того, конструктор копирования и оператор присваивания копии должны всегда принимать ссылку const (lvalue) в качестве параметра, а не const ссылка.

0 голосов
/ 01 марта 2020

Я не уверен, но поскольку ваш оператор + = является деструктивным, поскольку он не возвращает отдельную матрицу, а вместо этого перезаписывает предыдущую, добавление m4 к себе может вызвать странные ошибки, так как «this» добавление и возврат постоянно в движении. Нет никакой причины, почему оператор был бы цепным, так как компилятор в любом случае переводит цепочку в последовательности двоичных операций, поэтому проблема, скорее всего, связана с вашей структурой перегрузки + =. Кроме того, было бы более целесообразно добавить в + перегрузку и определить x + = x как x = x + x, а не наоборот.

...