vendredi 5 janvier 2018

How to properly write R-Value overloads for operators

For context, the actual class I'm working with is considerably more complicated and larger than what I'm showing here, but I'm using this just as an example.

struct Vector {
    int x, y;
    Vector() : Vector(0,0) {}
    Vector(int x, int y) : x(x), y(y) {}
};

I'd like to add operator overloads to allow Vectors to be added and subtracted from each other.

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

However, This code can allow unfortunate constructions:

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

So I rewrote the code to be mindful of whether the object is an L-value or R-value:

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

So my remaining question is, even though this code compiles and does what I'm expecting, is this code safe and free of unexpected Undefined Behavior?

Aucun commentaire:

Enregistrer un commentaire