Hans Dulimarta

C++ Move Semantics

I received the following email from a student: > I’m getting an infinite loop on the following code. It keeps going between these two functions whenever I try to assign a RationalNumber to another (and then crashes). I don’t see where the infinite copying is happening.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
/* copy constructor */
RationalNumber::RationalNumber (const RationalNumber& num)
{
    numerator = num.numerator;
    denominator = num.denominator;
}

/* copy assignment */
RationalNumber& RationalNumber::operator= (const RationalNumber& num)
{
    RationalNumber tmp{num};  // invoke lines 2-6 above
    std::swap (tmp, *this);

    return *this;
}

When we try to assign r1 to r2 in the following code

1
2
3
4
RationalNumber r1;         // this line invokes a constructor with no args
RationalNumber r2{15, 27}; // this line invokes a constructor with 2 args

r1 = r2;    // this line invokes the copy assignment at lines 9-19 above

Recall that a typical swap (x,y) requires a third (local) variable and the entire operation translates to the following sequence that requires one clone constructor and two assignments:

1
2
3
TypeOfX aux {x};    // optimized for "move" clone
x = y;              // optimized for "move" assign
y = aux;            // optimized for "move" assign

When possible std::swap will attempt to use the move clone constructor and move assignment (to avoid copying overhead). However, when RationalNumber provides neither “move constructor” nor “move assignment”, std::swap will fall back to the copy constructor and copy assignment. The latter invocation results in an infinite chain of recursive calls. (The std::swap call at line 12 attempts to make a recursive call to the copy assignment at line 9).