std :: move如何复制不可复制的对象?

时间:2013-12-24 22:30:48

标签: c++ c++11 move-semantics

请考虑以下代码。

#include <iostream>
#include <type_traits>

struct A
{
    int x;
    A() = default;
    ~A() = default;

    A(const A&) = delete;
    A &operator=(const A&) = delete;

    A(A &&) = default;
    A &operator=(A &&a) = default;
};

int main()
{
    std::cout << std::boolalpha;
    std::cout << std::is_copy_constructible<A>::value << std::endl;
    std::cout << std::is_copy_assignable<A>::value << std::endl;

    A a;
    a.x = 3;

    A b = std::move(a);

    std::cout << std::hex << a << " " << &b << std::endl;
    std::cout << a.x << " " << b.x;

    return 0;
}

示例输入是:

false
false
0xbfd1ee28 0xbfd1ee2c
3 3

正如您所看到的,虽然类b在设计上是不可复制的,但已成功复制变量A。我完全理解变量a不会移动到任何地方。 std::move调用可能应该构造一个首先复制对象a的rvalue-reference,但是嘿,在类定义中是不是禁止它?

有人可以解释一下,这里发生了什么?

2 个答案:

答案 0 :(得分:7)

为什么你认为有副本?您要求编译器为您生成移动构造函数。生成的移动构造函数将对所有元素进行成员移动。基本上有两种情况:

  1. 相应的成员有一个移动构造函数,在这种情况下,将应用此移动构造函数。
  2. 相应的成员没有移动构造函数,在这种情况下将复制构造函数。
  3. 移动内置类型就像复制它们一样,即它们不会对移动的来源做任何事情,例如,std::unique_ptr<T>会将空指针放入源中。

    使用时

    A b = std::move(a);
    

    使用为a生成的移动构造函数,为A提供可以移动构造的临时对象的外观。 rvalue-reference不是构造,而是简单地转换相同的对象。 std::move(a)相当于static_cast<A&&>(a)

答案 1 :(得分:4)

A b = a;

这不起作用,因为A是不可复制的。也就是说,因为a是一个左值表达式,这将尝试使用已经delete d的复制构造函数,因此它不会编译。

A b = std::move(a);

这样可行,因为A是可移动的。也就是说,因为std::move(a)是一个右值表达式,所以这将尝试使用default ed的移动构造函数,因此它将被编译。

仅在第一种情况下才尝试复制。第二个不涉及副本。仅仅因为b最终被构建,并不意味着它是从a复制的。相反,它已从<{1}} 移动