如何为运营商正确编写R-Value重载

Xir*_*ema 10 c++ undefined-behavior move-semantics c++11

对于上下文,我正在使用的实际类比我在这里展示的更复杂和更大,但我只是作为一个例子使用它.

struct Vector {
    int x, y;
    Vector() : Vector(0,0) {}
    Vector(int x, int y) : x(x), y(y) {}
};
Run Code Online (Sandbox Code Playgroud)

我想添加运算符重载以允许Vectors相互相加和相减.

Vector& operator+=(Vector const& v) {
    x += v.x;
    y += v.y;
    return *this;
}
Vector operator+(Vector const& v) const {
    return Vector(*this) += v;
}
Vector& operator-=(Vector const& v) {
    x -= v.x;
    y -= v.y;
    return *this;
}
Vector operator-(Vector const& v) const {
    return Vector(*this) -= v;
}
Run Code Online (Sandbox Code Playgroud)

但是,此代码可以允许不幸的结构:

int main() {
    Vector & a = Vector(1,2) += Vector(5,4);//This compiles and invokes undefined behavior!
    std::cout << a.x << ',' << a.y << std::endl;//This isn't safe!
}
Run Code Online (Sandbox Code Playgroud)

所以我重写了代码,要注意对象是L值还是R值:

Vector& operator+=(Vector const& v) & {
    x += v.x;
    y += v.y;
    return *this;
}
Vector&& operator+=(Vector const& v) && {
    return std::move(*this += v);
}
Vector operator+(Vector const& v) const {
    return Vector(*this) += v;
}
Vector& operator-=(Vector const& v) & {
    x -= v.x;
    y -= v.y;
    return *this;
}
Vector&& operator-=(Vector const& v) && {
    return std::move(*this -= v);
}
Vector operator-(Vector const& v) const {
    return Vector(*this) -= v;
}
Run Code Online (Sandbox Code Playgroud)

所以我剩下的问题是,即使这段代码编译并完成了我所期望的,这段代码是否安全并且没有意外的未定义行为?

int main() {
    //No Longer compiles, good.
    //Vector & a = Vector(1,2) += Vector(5,4);

    //Is this safe?
    Vector b = Vector(1,2) += Vector(5,4);

    //Other cases where this code could be unsafe?
}
Run Code Online (Sandbox Code Playgroud)

Yak*_*ont 6

以下是重载这些运算符的相对标准方法:

Vector& operator+=(Vector const& v)& {
  x += v.x;
  y += v.y;
  return *this;
}
friend Vector operator+(Vector lhs, Vector const& v) {
  lhs+=v;
  return std::move(lhs); // move is redundant yet harmless in this case
}
Vector& operator-=(Vector const& v)& {
  x -= v.x;
  y -= v.y;
  return *this;
}
friend Vector operator-(Vector lhs, Vector const& v) {
  lhs -= v;
  return std::move(lhs); // move is redundant yet harmless in this case
}
Run Code Online (Sandbox Code Playgroud)

请注意,在许多行上,+或者-上面生成的副本比重载更少.

a+b+c变为(a+b)+c,并且返回值a+b直接被省略到lhs参数中+c.你的第一行+无论如何都要创建一个副本,所以签名中的额外副本是无害的.

除非你有充分的理由,禁止+==rvalues. int不支持它,你也不应该支持它.