请考虑以下代码。
#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,但是嘿,在类定义中是不是禁止它?
有人可以解释一下,这里发生了什么?
答案 0 :(得分:7)
为什么你认为有副本?您要求编译器为您生成移动构造函数。生成的移动构造函数将对所有元素进行成员移动。基本上有两种情况:
移动内置类型就像复制它们一样,即它们不会对移动的来源做任何事情,例如,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}} 移动。