假设我有一个(普通的)类,它是可移动构造和可移动分配但不可复制构造或可复制分配:
class movable
{
public:
explicit movable(int) {}
movable(movable&&) {}
movable& operator=(movable&&) { return *this; }
movable(const movable&) = delete;
movable& operator=(const movable&) = delete;
};
这很好用:
movable m1(movable(17));
当然,这不起作用,因为m1
不是右值:
movable m2(m1);
但是,我可以将m1
包裹在std::move
中,并将其转换为右值引用,以使其有效:
movable m2(std::move(m1));
到目前为止,这么好。现在,假设我有一个(同样微不足道的)容器类,它包含一个值:
template <typename T>
class container
{
public:
explicit container(T&& value) : value_(value) {}
private:
T value_;
};
然而,这不起作用:
container<movable> c(movable(17));
编译器(我尝试过clang 4.0和g ++ 4.7.2)抱怨我试图在movable
的初始化列表中使用container
删除的拷贝构造函数。同样,将value
包裹在std::move
中可以使其正常工作:
explicit container(T&& value) : value_(std::move(value)) {}
但是为什么在这种情况下需要std::move
? value
类型movable&&
已经不是value_(value)
了吗? movable m1(movable(42))
与{{1}}的区别如何?
答案 0 :(得分:16)
那是因为value
是一个命名变量,因此是一个左值。 std::move
需要将其强制转换为右值,这样才会导致T
的move-constructor重载匹配。
换句话说:右值引用可以绑定到右值,但它本身不是右值。它只是一个引用,在表达式中它是一个左值。从中创建一个rvalue表达式的唯一方法是通过强制转换。
答案 1 :(得分:5)
value_(value)
与movable m1(movable(42))
的区别如何?
命名的右值引用是左值(因此将绑定到已删除的副本ctor),而临时值则是右值(prvalue是特定的)。
§5 [expr] p6
[...]通常,此规则的作用是将命名的右值引用视为左值,对对象的未命名右值引用被视为xvalues [...]
以及示例:
A&& ar = static_cast<A&&>(a);
表达式
ar
是左值。
以上引用来自非规范性说明,但是是一个充分的解释,因为第5条的其余部分解释了哪些表达式仅创建xvalues †(又名,只有指定的表达式,否则将创建xvalues)。有关详尽列表,另请参阅here。
†xvalues是rvalues的一个子组,prvalues是另一个子组。有关说明,请参阅this question。