简短回答
因为它们是可复制的。
详细回答
我们首先要澄清“移动”实际上是什么意思。
冯·诺伊曼机不会移动数据:它们只是“复制”。数据从一个内存位置复制到另一个位置。从来没有“移动”过。
但在更高的抽象层次上,数据可以只是指向其他数据的指针。当您复制指针并使已复制的指针失效时,所涉及的数据被称为从一个“所有者”移动到另一个“所有者”。
更普遍地说,复制一个值(例如指针中包含的地址)并销毁原始值使其设置为可识别的“无效”状态的操作被称为“移动”操作。
在C ++方面,我们可以区分不同类型的对象:
- 只包含简单值的对象。
- 只包含简单指针或引用的对象,它们“拥有”引用的内容。
- 只包含简单指针或引用的对象,它们不“拥有”引用的内容。
- 包含巨大值的对象。
- 表示物理实体或操作系统(更一般的“托管平台”实体)的对象。
对于所有这些类型,"复制"和"移动"的概念可能具有不同的语义意义,对于其中一些对象,其中一个操作可能根本没有意义。
现在考虑第一种类型的对象:
int a=5
c = a
c = std::move(a)
你期望移动后
a
的值是多少?
c = a+b
呢?a和b应该被“移动”到
operator+
中吗?
现在考虑类型2对象:
std::unique_ptr<int> pa(new int(5)), pb;
pb = std::move(pa);
这里有两个智能指针(它们都将在作用域退出时被销毁),以及一个整数。有一种操作(在本例中是delete
)只能执行一次,因此只有一个指针必须保留对整数的“所有权”。这种情况下,“复制”是无意义的,移动是唯一支持的操作。
现在考虑第三种类型的对象:
std::list<int> lst = { 1,2,3,4 };
auto i = lst.begin();
auto j = i;
*j = *i+5;
++i;
*i = *j;
这完全有道理:它只会使列表变为
{6,6,3,4}
。
迭代器不拥有其引用的内容:可能有多个迭代器都指向同一值。复制是有意义的,但移动则不然:如果我们将
i 移入
j (而不是复制),那么就不再可能使用 *i 和 ++i 了。
现在考虑类型为4的对象:
class A
{
int m[15000000];
public:
int& operator[](unsigned x) { return m[x]; }
const int& operator[](unsigned x) const { return m[x]; }
};
这样一个巨大的对象在大多数系统中分配到栈上可能会有问题。它很可能会留在堆上,并由(智能)指针拥有/引用。它的地址将在其指针之间移动,但对象本身不可移动。它仍然可以被复制。
还有另一种微妙的情况:当A本身是指向动态分配的巨大数组的指针时:这与std::vector相同:它是可移动的,因为它本身是拥有动态分配数据的“智能指针”,但也可以被复制,因为可能存在需要新的不同副本的情况。
现在考虑类型5:
class window
{
private:
HWND handle;
public:
window() :handle(CreateWindow(....))
{ .... }
~window() { DestroyWindow(handle); }
};
这里的window
实例代表屏幕上存在的一个window。 "复制"或"移动"是什么意思?
这很可能是mutex
、condition_variable
等情况,其中复制和移动都被禁用。