Let's consider the following code:
class X {
std::vector<int> _v;
public:
X(std::vector<int>&& v): _v(std::move(v)) {}
};
The compiler calls this constructor only for objects that can be moved. So why not just define an rvalue references to be rvalue expressions and don't write every time std::move for them?
The ctor member initialization list would look like:
_v(v)
But this would still be a move, not a copy.
An rvalue reference behaves just like an lvalue reference except that it can bind to a temporary (an rvalue), whereas you can not bind a (non const) lvalue reference to an rvalue.
Rvalue references enable you to write one version of a function that accepts arbitrary arguments. Then that function can forward them to another function as if the other function had been called directly.
rvalue references have two properties that are useful: rvalue references extend the lifespan of the temporary object to which they are assigned. Non-const rvalue references allow you to modify the rvalue.
An lvalue reference can bind to an lvalue, but not to an rvalue.
While it is somewhat unfortunate to require the std::move
in this common case, it was believed that an implicit move causing a run-time error in a few cases would be more harmful.
For example:
class Y
{
public:
Y(const std::vector<int>& v);
};
class X {
std::vector<int> v_;
Y y_;
public:
X(std::vector<int>&& v): v_(v), y_(v) {}
};
In this modified example the constructor of X
uses v
twice. If the first use of v
implicitly moved, then the second use of v
would quite likely not be getting the expected value.
Thus to avoid the accidental "use after move", if it has a name, then it can be used more than once, and is thus safer to treat it as an lvalue.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With