18

这个讨论的问题中,何时在 C++11 中使类型不可移动,我发现 Scott Meyers 在comp.std.c++上有类似的问题,其中 SG 下面列出的类类型在 C++11 库中是不可移动的。

  • 所有互斥锁类型(recursive_mutex,timed_mutex,recursive_timed_mutex,
  • 条件变量
  • 类型信息
  • 错误类别
  • 语言环境::方面
  • 随机设备
  • 种子序列
  • 参考包装
  • 期间
  • 时间点
  • - 所有迭代器/迭代器适配器
  • ios_base
  • basic_istream::sentry
  • basic_ostream::哨兵
  • 所有原子类型
  • 一次标志

问题是为什么all iterators / iterator adaptors不可移动?

4

4 回答 4

14

从该标准批准前一年开始,该帖子已过时。海报是活跃的委员会成员丹尼尔克鲁格勒,有点政治游说:

这些是不可移动的,而且可能更多是偶然的,因为隐式生成的移动操作的规则在匹兹堡会议上才得到澄清。已打开一般库问题

http://www.open-std.org/jtc1/sc22/wg21/docs/lwg-active.html#1331

以应对图书馆缺乏移动支持。请务必联系您的国家机构代表,因为我认为这个问题不足以替代国家机构对 FCD 的评论。

换句话说,所有这些不可移动的类型对于标准来说都是一个引人注目的错误,他希望 Usenet 观众中的读者要求在标准正式发布之前解决问题。

该缺陷已移至“关闭”列表。解决方案是(为方便起见提供了链接):

查看规范的库部分并合并新添加的核心功能 Move Special Member Functions ( N3044 )。

由于 N3044 是一种大量的材料,因此很容易理解为什么它对于这种基本功能的工作至关重要。

迭代器,以及任何其他具有简单值语义的东西,比如std::durationand std::time_point当然是可移动的。正如其他人所提到的,可复制性意味着可移动性,如果不这样做,语言就会被破坏。这篇文章当时并没有错;而是在争论未完成的语言的破碎性。

于 2013-01-14T09:52:42.803 回答
10

我认为您在不可移动类型列表中包括:“将移动实现为普通复制的类”。. 迭代器被认为是复制成本低的轻量级对象。为他们指定一个移动操作员是没有意义的。例如,std::vector<T>::iterator本质上只是一个包装好的T*,复制它们就像移动一样便宜。

于 2013-01-14T07:32:44.950 回答
2

关于迭代器的“不可移动”可能意味着类定义不包含用户声明的移动操作。但是迭代器仍然是可复制的。因此,移动请求仍然有效,并且依赖于复制。因此,在移动操作与复制操作完全相同的情况下,没有理由提供移动操作。对于典型的迭代器实现,没有什么可以针对 wrt 移动进行优化。

于 2013-01-14T09:23:08.523 回答
1

简短的回答

因为它们是可复制的。

长答案

我们首先要弄清楚“移动”的真正含义。冯纽曼机器不移动数据:你只是“复制”。数据从一个内存位置复制到另一个内存位置。从未“移动​​”过。

但在更高的抽象级别,数据可能只是指向其他数据的指针。当您复制使复制的指针无效的指针时,所引用的数据被称为从一个“所有者”“移动”到另一个“所有者”。

更一般地,复制一个值(包含在指针中的地址)并销毁将其设置为可识别的“无效”的原始值的操作被称为“移动”。

就 C++ 而言,可以区分不同类型的对象:

  1. 那些只包含一个简单的值。
  2. 那些只包含一个“拥有”它们所指内容的简单指针或引用
  3. 那些只包含一个简单的指针或引用而不是“欠”他们所指的东西
  4. 那些包含巨大价值的东西。
  5. 代表物理实体或操作系统(更一般的“托管平台”)实体的实体。

对于所有这些类型,“复制”和“移动”的概念可能具有不同的语义含义,并且对于其中一些操作,一种或另一种可能根本就没有意义。

现在考虑类型 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 }. 迭代器不拥有它们所指的内容:可能有许多迭代器都引用相同的值。复制是有意义的,但移动没有:如果我们移动ij(而不是复制)没有 *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表示屏幕上存在的一个窗口。“复制”或“移动”是什么意思?

这很可能是 , 等的情况mutexcondition_variable其中复制和移动都被禁用。

于 2013-01-14T08:38:59.660 回答