为什么我需要使用std :: Move在Move-Constructor的初始化列表中

Why do I need to use std::move in the initialization list of a move-constructor?

本文关键字:Move-Constructor 初始化 列表 Move 为什么 std      更新时间:2023-10-16

假设我有一个(琐碎的)类,它是可移动的构建和移动 - 可分配但不可复制构建或副本分配的类别:

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不是RVALUE:

movable m2(m1);

,但是,我可以将m1包装在std::move中,将其施放为rvalue-reference,以使其起作用:

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)抱怨说我正在尝试在container的初始化列表中使用movable的删除复制构建器。同样,将value包装在std::move中使其起作用:

    explicit container(T&& value) : value_(std::move(value)) {}

但是为什么在这种情况下需要std::movevalue不是movable&&类型吗?value_(value)movable m1(movable(42))有何不同?

那是因为 value是命名变量,因此是lvalue。需要std::move将其归还为RVALUE,以便将T的移动构造人过载匹配。

用另一种方式说:rvalue参考可以 bind 与rvalue结合,但它本身并不是一个rvalue。这只是一个参考,在表达中,它是一个lvalue。从中创建一个表达式的唯一方法是铸造。

value_(value)movable m1(movable(42))有何不同?

命名的rvalue参考是一个lvalue(因此将绑定到已删除的副本ctor),而临时是一个rvalue(特定于特定的prvalue)。

§5 [expr] p6

[...]通常,此规则的效果是,命名的rvalue参考被视为lvalues,对对象的未命名rvalue引用被视为xvalues [...]

与示例同样的

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

表达式 ar是lvalue。

上面的引号来自非规范性笔记,但是一个足够的解释,因为第5条的其余部分进行说明了哪些表达式唯一 create xvalues (aka),只有指定的表达式和其他任何人都不会创建xvalues)。另请参阅此处以获取详尽的列表。

†xvalues是一个子组,其中Prvalues是另一个子组。请参阅此问题以进行解释。