Let's say I have a (trivial) class, which is move-constructible and move-assignable but not copy-constructable or copy-assignable:
class movable
{
public:
explicit movable(int) {}
movable(movable&&) {}
movable& operator=(movable&&) { return *this; }
movable(const movable&) = delete;
movable& operator=(const movable&) = delete;
};
This works fine:
movable m1(movable(17));
This, of course, does not work, because m1
is not an rvalue:
movable m2(m1);
But, I can wrap m1
in std::move
, which casts it to an rvalue-reference, to make it work:
movable m2(std::move(m1));
So far, so good. Now, let's say I have a (equally trivial) container class, which holds a single value:
template <typename T>
class container
{
public:
explicit container(T&& value) : value_(value) {}
private:
T value_;
};
This, however, does not work:
container<movable> c(movable(17));
The compiler (I've tried clang 4.0 and g++ 4.7.2) complains that I'm trying to use movable
's deleted copy-constructor in container
's initialization list. Again, wrapping value
in std::move
makes it work:
explicit container(T&& value) : value_(std::move(value)) {}
But why is std::move
needed in this case? Isn't value
already of type movable&&
? How is value_(value)
different from movable m1(movable(42))
?
A named rvalue reference is an lvalue (and will thus bind to the deleted copy ctor), while a temporary is, well, an rvalue (a prvalue to be specific).
§5 [expr] p6
Aswell as from the example:
The above quotes are from non-normative notes, but are an adequate explanation, since the rest of clause 5 goes and explains which expressions only create xvalues† (aka, only the specified expressions and none else will create xvalues). See also here for an exhaustive list.
† xvalues are one subgroup of rvalues, with prvalues being the other subgroup. See this question for an explanation.
That's because
value
is a named variable, and thus an lvalue. Thestd::move
is required to cast it back into an rvalue, so that it will cause move-constructor overload ofT
to match.To say it another way: An rvalue reference can bind to an rvalue, but it is not itself an rvalue. It's just a reference, and in an expression it is an lvalue. The only way to create from it an expression that is an rvalue is by casting.