简短的回答
因为它们是可复制的。
长答案
我们首先要弄清楚“移动”的真正含义。冯纽曼机器不移动数据:你只是“复制”。数据从一个内存位置复制到另一个内存位置。从未“移动”过。
但在更高的抽象级别,数据可能只是指向其他数据的指针。当您复制使复制的指针无效的指针时,所引用的数据被称为从一个“所有者”“移动”到另一个“所有者”。
更一般地,复制一个值(包含在指针中的地址)并销毁将其设置为可识别的“无效”的原始值的操作被称为“移动”。
就 C++ 而言,可以区分不同类型的对象:
- 那些只包含一个简单的值。
- 那些只包含一个“拥有”它们所指内容的简单指针或引用
- 那些只包含一个简单的指针或引用而不是“欠”他们所指的东西
- 那些包含巨大价值的东西。
- 代表物理实体或操作系统(更一般的“托管平台”)实体的实体。
对于所有这些类型,“复制”和“移动”的概念可能具有不同的语义含义,并且对于其中一些操作,一种或另一种可能根本就没有意义。
现在考虑类型 1 对象:
int a=5; c=0;
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
在这种情况下是 )只能执行一次,因此只有一个指针必须保留整数的“所有权”。这就是“复制”没有意义的情况,移动是唯一支持的操作
现在考虑类型 3 对象:
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]; //15 million integers
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
表示屏幕上存在的一个窗口。“复制”或“移动”是什么意思?
这很可能是 , 等的情况mutex
,condition_variable
其中复制和移动都被禁用。