Invalid use of rvalue references from old code: how should I update it to modern, correct usage?

161 Views Asked by At

I am porting a large project from C++ Builder 2010 to XE4, and have just come across this code. It compiled, ran, and apparently worked in CB2010, and according to the revision log was added when we were still using C++ Builder 2007. Neither of these compilers supported rvalue references.

VOperandValue& VOperandValue::operator=(VOperandValue&& oOther) {
    // Assignment operator takes full control of all pointers etc, and sets oOther
    // to not own anything
    if (static_cast<void*>(this) != static_cast<void*>(&oOther)) {
        CopyAndTakeOwnershipFrom(oOther);
    }
    return *this;
}

void VOperandValue::CopyAndTakeOwnershipFrom(VOperandValue&& oOther) {
    // Lots of assignments to self's members, and clearing fields of oOther
}

Two things to note:

  • operator= changing the copied-from object is deliberate. This class wraps resources that can only be owned by one object at a time. It's beyond the scope of this question, but the behaviour, while odd, is as designed. There is also a copy constructor and a few others methods that have been changed similarly. The methods are intended for use where the copied-from objects are either about to become invalid (eg destroyed) or will be re-used to hold something new. I have no idea why static_cast<void*> is necessary for comparing the pointers to objects of the same type; it seems a very odd thing to do, to me.
  • The code was changed from using standard references (ie VOperandValue& oOther) to rvalue-references. Despite support for rvalue references only being added in C++Builder XE, the version after C++Builder 2010, the compiler happily accepted and compiled it, and the code appears to have worked when run.

Now this code is loaded into XE4, it fails to compile on the line CopyAndTakeOwnershipFrom(oOther) with the following errors:

[bcc32 Error] OperandValue.cpp(178): E2559 Can't initialize rvalue reference of type 'VOperandValue' with lvalue of type 'VOperandValue'

[bcc32 Error] OperandValue.cpp(178): E2342 Type mismatch in parameter 'oOther' (wanted 'VOperandValue &&', got 'VOperandValue')

(I don't understand these errors, since line 178 is the line CopyAndTakeOwnershipFrom(oOther);, and oOther appears to definitely have been defined in the method parameter list as an rvalue-reference. Why then is it having trouble with an lvalue non-r-ref when passing the same variable?)

I have two questions, a primary practical question and a secondary curiosity question:

  1. Primary: The coder who changed this code from using standard references to rvalue-references presumably did so thinking that move semantics were best in this situation. I can understand that, though assignment involves copying pointer values only, which isn't much work. How would I, if they're suitable, correctly use rvalue-references for this code?

  2. Secondary: (Curiosity only.) What did the 2007 and 2010 compilers make of this code? Was it read as a reference to a reference? Did the two & operators coalesce and become a single reference? Since it was, presumably, invalid syntax, yet it compiled and worked fine, what on earth was it doing?

1

There are 1 best solutions below

0
On

As a solution to your problem you can use std::forward.

Just change your code to:

#include <utility>   
CopyAndTakeOwnershipFrom(std::forward<VOperandValue>(oOther));

The problem arises because you want to forward move semantics through 2 functions but in the first function the moved object gets a name. With a name its not an rvalue anymore without std::forward but an lvalue that cannot be used for a T&& parameter in CopyAndTakeOwnershipFrom. That it worked before in C++ Builder 2010 seems to be a bug there which got fixed in XE4.