C++中奇怪的赋值/乘法运算符行为

Strange assignment / multiplication operator behaviour in C++

本文关键字:运算符 赋值 C++      更新时间:2023-10-16

有人可以解释一下我的运算符有问题吗:

Matrix3D Matrix3D::operator*(Matrix3D& m) {
    Matrix3D ret;
    for(int i=0;i<4;i++) {
        for(int j=0;j<4;j++) {
            ret._data[i][j]=0.0;
            for(int k=0;k<4;k++) {
                ret._data[i][j] += (this->_data[i][k]*m._data[k][j]);
            }
        }
    }
    return ret;
}
Matrix3D& Matrix3D::operator=(Matrix3D& m) {
    if(this==&m) {
        return *this;
    }
    for(int i=0;i<4;i++) {
        for(int j=0;j<4;j++) {
            this->_data[i][j] = m._data[i][j];
        }
    }
    return *this;
}
Matrix3D Matrix3D::Rotation(double ax, double ay, double az) {
    Matrix3D rotX;
    Matrix3D rotY;
    Matrix3D rotZ;
    rotX(
        1,          0,          0,          0,
        0,          cos(ax),    -sin(ax),   0,
        0,          sin(ax),    cos(ax),    0,
        0,          0,          0,          1
    );
    rotY(
        cos(ay),    0,          sin(ay),    0,
        0,          1,          0,          0,
        -sin(ay),   0,          cos(ay),    0,
        0,          0,          0,          1
    );
    rotZ(
        cos(az),    -sin(az),   0,          0,
        sin(az),    cos(az),    0,          0,
        0,          0,          1,          0,
        0,          0,          0,          1
    );
    // Matrix3D ret;
    // ret = rotX*rotY*rotZ;
    // This doesn't work
    // C:(...)Matrix3D.cpp|100|error: no match for 'operator=' in 'ret = Matrix3D::operator*(Matrix3D&)(((Matrix3D&)(& rotZ)))'|
    // however this does work
    Matrix3D ret = rotX*rotY*rotZ;
    return ret;
}

如上面的代码所述,类似

    Matrix3D ret;
    ret = rotX*rotY*rotZ;

将导致

    C:(...)Matrix3D.cpp|100|error: no match for 'operator=' in 'ret = Matrix3D::operator*(Matrix3D&)(((Matrix3D&)(& rotZ)))'|

编译错误,而类似

    Matrix3D ret = rotX*rotY*rotZ;

将在没有任何警告或错误的情况下编译(不知道矩阵是否正确,还没有检查过......

实际上,这两个运算符都被错误地声明了。它们应采用const引用以允许接受右值。

Matrix3D Matrix3D::operator*(const Matrix3D& m) const
//                           ^^^^^              ^^^^^
//                                              this too, since you're not
//                                              changing the LHS during '*'.
Matrix3D& Matrix3D::operator=(const Matrix3D& m)
//                            ^^^^^

您会发现*不适用于rotX*(rotY*rotZ)

编译Matrix3D ret = rotX*rotY*rotZ;原因是根本没有调用operator=。它只是调用 Matrix3D 的复制构造函数来获得乘法的结果。乘法有效,因为rotX * rotY * rotZ被重写为

rotX.operator*(rotY).operator*(rotZ)

rotYrotZ都是左值,因此它们可以绑定到Matrix3D&

operator*operator=的正确声明:

Matrix3D  Matrix3D::operator* (Matrix3D const& m) const;
Matrix3D& Matrix3D::operator= (Matrix3D const& m);

阅读材料:

  • 什么是左值/右值?
  • 什么是常量正确性,为什么 ::operator* 应该是恒量?

您的operator=接受Matrix3D&尽管rotX*rotY*rotX会产生右值,因此您无法绑定对它的引用。

为了使工作,你的operator=应该接受一个Matrix3D const&,它可以绑定到左值右值

即使它看起来像Obj a = b使用Obj::operator=它不是,它也将使用通常称为复制构造函数的东西。在此处阅读更多相关信息。

将赋值运算符的签名更改为 const-correct:

Matrix3D& Matrix3D::operator=(Matrix3D const & m)
//                                     ^^^^^

在示例代码中,赋值运算符想要绑定到临时引用,而临时运算符无法绑定到非常量引用。相比之下,由第二段代码调用的复制构造函数确实接受 const 引用,因此没有错误。

Matrix3D& Matrix3D::operator=(Matrix3D& m)

您请求右侧参数为 l 值。它必须是现有变量,不能是临时值。请考虑改用const Matrix3D& m

Matrix3D ret = rotX*rotY*rotZ;之所以有效,是因为它根本不使用operator=,而是使用默认的构造函数Matrix3D::Matrix3D(const Matrix3D&)