【问题标题】:C++ - Making operator overload chainableC++ - 使运算符重载可链接
【发布时间】:2020-03-01 19:59:28
【问题描述】:

所以我试图为我的“矩阵”类(+ 和 +=)重载两个运算符。我正在尝试使+ 可链接和+= 不可链接:

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

编辑2: 添加了更多上下文(根据请求、标头、构造函数等)

标题:

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

【问题讨论】:

  • 您的operator = 正确吗?请将其包含在问题中。
  • 请提供完整的minimal reproducible example,至少是Matrix的特殊成员函数的定义。您的复制/移动构造函数或复制/移动分配似乎已损坏。 += 在您的示例中也可以链接(这是一件好事)。
  • @Schytheron 复制赋值运算符应始终将其参数作为const 引用。 (顺便说一句。您可能还希望operator+ 采用const 引用的第二个参数。)复制构造函数和析构函数看起来如何?请包括至少Matrix的定义和所有特殊成员函数的定义。将您的问题减少到minimal reproducible example
  • 您的赋值运算符可以只用 4 行调用 std::swap 的代码来编写。使用copy / swap idiomMatrix t(obj); std::swap(t.m_rows, m_rows); std::swap(t.m_cols, m_cols); std::swap(t.m_capacity, m_capacity); std::swap(t.m_vec, m_vec); return *this;
  • 你可以通过使用vector而不是裸新闻来消除一整类错误

标签: c++ matrix operator-overloading chaining method-chaining


【解决方案1】:

您的移动构造函数和移动赋值运算符都没有实现正确的语义(您似乎已经意识到这一点),导致稍后出现 UB。 (我没有费心检查确切的位置。)

我猜你假设你实际上并没有调用这些运算符,但这是错误的。

您在 = 的符号处调用移动赋值运算符

M2 = M4 + M4 + M4;

因为右边是一个prvalue(operator+返回按值),它可以绑定到一个右值引用。

(在 C++17 之前)您在同一行中的第二个 + 处调用(可能已省略)移动构造函数来构造 operator+ 的第一个参数,因为第一个 + 产生一个纯右值.

如果您打算稍后实现移动操作,并且暂时可以使用复制实现,那么请不要在您的类中声明移动操作。然后编译器会选择你的复制实现。

此外,复制构造函数和复制赋值运算符应该始终const(左值)引用作为参数,而不是非const引用。

【讨论】:

  • 我修复了移动构造函数和移动赋值运算符,一切似乎都运行良好。除了一个问题。我的 += 运算符是可链接的,我不希望这样。有什么简单的方法可以让 += 不可链接,同时仍保持 + 运算符可链接?
  • @Schytheron 我看不出有什么好的理由让+= 不可链接。这是非常不习惯的,使用您的课程的每个人都会对这种行为感到惊讶。但是如果你想这样,你需要将operator+=的返回类型更改为void。当然,那时您将无法使用return M1 += M2;,而是需要编写M1 += M2; return M1;,而这种冗余正是为什么将+= 设为可链接是一个好主意。
【解决方案2】:

我不确定,但是由于您的 += 运算符具有破坏性,因为它不会返回单独的矩阵而是覆盖前一个矩阵,因此将 m4 添加到自身可能会导致奇怪的错误,因为“this”就是不断增加和回报不断变化。由于编译器无论如何都会将链带入二进制操作序列,因此没有理由使运算符可链接,因此问题很可能与您的 += 重载结构有关。此外,在 + 重载中添加并将 x += x 定义为 x = x + x 会更有意义,而不是相反。

【讨论】:

  • 不,事实并非如此。 operator+ 按值获取第一个参数(因此是副本)并仅对其进行修改。然后它还返回该值的副本。
  • 实际上,由于您将定义打印为采用 const Matrix& 它是通过引用传递的!
  • 建议按照此代码中的+= 定义+stackoverflow.com/questions/4421706/…
  • @user12962917 正如我在(更正的)第一条评论中提到的,operator+ 正在制作一个它调用operator+= 的副本。因此M4 永远不会被修改。
  • @user12962917 这根本不是真的。 operator+ 被声明为 Matrix&lt;T&gt; operator+(Matrix&lt;T&gt; M1, Matrix&lt;T&gt;&amp; M2) 。第一个参数,对应M4 + M4 的左侧不是引用,因此运算符重载中的M1 将是M4copy。然后运算符重载调用M1 += M2,其中只有M2 引用M4。但是operator+= 将第二个参数作为const 引用并没有修改它,因此M4M2M1 都没有修改,这是它的副本。
猜你喜欢
  • 2012-03-23
  • 1970-01-01
  • 1970-01-01
  • 2021-09-04
  • 1970-01-01
  • 2011-12-03
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多