C++ - 使运算符重载可链接
C++ - Making operator overload chainable
所以我试图为我的 "Matrix" class(+ 和 +=)重载两个运算符。我正在尝试使 +
可链接和 +=
non-chainable:
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:
添加了更多上下文(根据要求,header、构造函数等)
Header:
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;
}
我不确定,但由于您的 += 运算符具有破坏性,因为它不会 return 一个单独的矩阵而是覆盖前一个矩阵,将 m4 添加到自身可能会导致奇怪的错误,因为正在添加的“this”和 return 不断变化。没有理由可以将运算符链接起来,因为编译器无论如何都会将链接带入二进制操作序列,因此问题很可能出在您的 += 重载结构上。此外,在 + 重载中添加并将 x += x 定义为 x = x + x 而不是相反更有意义。
你的移动构造函数和你的移动赋值运算符都没有实现正确的语义(你似乎意识到了这一点),导致稍后出现 UB。 (我没有费心检查具体位置。)
我猜你假设你实际上并没有调用这些运算符,但那是错误的。
您正在
的 =
符号处调用移动赋值运算符
M2 = M4 + M4 + M4;
因为右侧是纯右值(operator+
returns 按值),它可以绑定到右值引用。
(C++17 之前)您在同一行的第二个 +
处调用(可能省略的)移动构造函数来构造 operator+
的第一个参数,因为第一个 +
产生纯右值。
如果您打算稍后实现移动操作,并且暂时可以使用复制实现,那么根本不要在 class 中声明移动操作。然后编译器将选择您的复制实现。
此外,复制构造函数和复制赋值运算符应该始终将const
(左值)引用作为参数,而不是非const
参考。
所以我试图为我的 "Matrix" class(+ 和 +=)重载两个运算符。我正在尝试使 +
可链接和 +=
non-chainable:
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: 添加了更多上下文(根据要求,header、构造函数等)
Header:
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;
}
我不确定,但由于您的 += 运算符具有破坏性,因为它不会 return 一个单独的矩阵而是覆盖前一个矩阵,将 m4 添加到自身可能会导致奇怪的错误,因为正在添加的“this”和 return 不断变化。没有理由可以将运算符链接起来,因为编译器无论如何都会将链接带入二进制操作序列,因此问题很可能出在您的 += 重载结构上。此外,在 + 重载中添加并将 x += x 定义为 x = x + x 而不是相反更有意义。
你的移动构造函数和你的移动赋值运算符都没有实现正确的语义(你似乎意识到了这一点),导致稍后出现 UB。 (我没有费心检查具体位置。)
我猜你假设你实际上并没有调用这些运算符,但那是错误的。
您正在
的=
符号处调用移动赋值运算符
M2 = M4 + M4 + M4;
因为右侧是纯右值(operator+
returns 按值),它可以绑定到右值引用。
(C++17 之前)您在同一行的第二个 +
处调用(可能省略的)移动构造函数来构造 operator+
的第一个参数,因为第一个 +
产生纯右值。
如果您打算稍后实现移动操作,并且暂时可以使用复制实现,那么根本不要在 class 中声明移动操作。然后编译器将选择您的复制实现。
此外,复制构造函数和复制赋值运算符应该始终将const
(左值)引用作为参数,而不是非const
参考。