I read a beautiful article on the move semantics in C++11. This article is written in a very intuitive way. The example class in the article is given below.
class ArrayWrapper
{
public:
// default constructor produces a moderately sized array
ArrayWrapper ()
: _p_vals( new int[ 64 ] )
, _metadata( 64, "ArrayWrapper" )
{}
ArrayWrapper (int n)
: _p_vals( new int[ n ] )
, _metadata( n, "ArrayWrapper" )
{}
// move constructor
ArrayWrapper (ArrayWrapper&& other)
: _p_vals( other._p_vals )
, _metadata( other._metadata )
{
other._p_vals = NULL;
}
// copy constructor
ArrayWrapper (const ArrayWrapper& other)
: _p_vals( new int[ other._metadata.getSize() ] )
, _metadata( other._metadata )
{
for ( int i = 0; i < _metadata.getSize(); ++i )
{
_p_vals[ i ] = other._p_vals[ i ];
}
}
~ArrayWrapper ()
{
delete [] _p_vals;
}
private:
int *_p_vals;
MetaData _metadata;
};
Clearly in the above move constructor implementation, the movement doesn’t happen for the the embedded element _metadata. To facilitate this the trick is to use the std::move() method like this.
ArrayWrapper (ArrayWrapper&& other)
: _p_vals( other._p_vals )
, _metadata( std::move( other._metadata ) )
{
other._p_vals = NULL;
}
So far, so good.
The standard says:
§5 (C++11 §5[expr]/6):
[ Note: An expression is an xvalue if it is:
the result of calling a function, whether implicitly or explicitly,
whose return type is an rvalue reference to object type,a cast to an rvalue reference to object type,
a class member access expression designating a non-static data member
of non-reference type in which the object expression is an xvalue, ora
.*pointer-to-member expression in which the first operand is an
xvalue and the second operand is a pointer to data member.
My question:
Now, the variable other in the move constructor is an xvalue (am I right?). Then according to the last rule above, other._metadata should also be an xvalue. And hence the compiler can implicitely use the move constructor of _metadata‘s class. So, no need to std::move here.
What am I missing?
Your assumption is not really true. The argument to the constructor is an
xvalue, which allows the rvalue-reference to be bound, but once the rvalue-reference is bound, inside the constructor, it is no longer anxvaluebut anlvalue. Conceptually, the object at the call place is expiring, but inside the constructor and until it completes it is no longer expiring, as it can be used later within the constructor block.In [1], the expression
f()refers to a temporary, that will expire after the call to the constructor, so it can be bound by an rvalue-reference.Inside the constructor,
otheris not expiring, it will be alive for each and every instruction of the constructor. If the compiler allowed moving in [2], than a potential further use of the variable in [3] would be invalid. You have to explicitly tell the compiler that you want the value to expire now.