14

假设我有一个(平凡的)类,它是可移动构造和可移动分配的,但不能复制构造或复制分配:

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);

但是,我可以换m1std::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))

4

2 回答 2

16

那是因为value是一个命名变量,因此是一个左值。需要将std::move其转换回右值,以便它会导致T匹配的移动构造函数重载。

换一种说法:右值引用可以绑定到右值,但它本身不是右值。它只是一个引用,在表达式中它是一个左值。从它创建一个右值表达式的唯一方法是强制转换。

于 2012-10-30T12:43:20.957 回答
5

有什么value_(value)不同movable m1(movable(42))

命名的右值引用是左值(因此将绑定到已删除的副本ctor),而临时值是右值(具体而言是纯右值)。

§5 [expr] p6

[...] 通常,此规则的效果是命名的右值引用被视为左值,而对对象的未命名右值引用被视为 xvalue [...]

以及来自示例:

A&& ar = static_cast<A&&>(a);

表达式ar是一个左值。

上面的引用来自非规范性注释,但是是一个充分的解释,因为第 5 节的其余部分解释了哪些表达式创建 xvalues †</sup>(也就是只有指定的表达式,没有其他表达式会创建 xvalues)。另请参阅此处以获取详尽列表。

† xvalues 是 rvalues 的一个子组,prvalues 是另一个子组。请参阅此问题以获取解释。

于 2012-10-30T12:47:01.650 回答