移动语义是不完整的?

时间:2014-01-13 07:12:14

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

在复制效率低下的情况下,移动语义替换复制语义。复制语义完全处理可复制对象,包括const对象。

在c ++ 11中已经存在无数的不可复制对象,例如std :: unique_ptr。这些对象完全依赖于移动语义,因为从对象移动允许使其无效。这对于像RAII这样的流行设计模式很重要(imho)。

将const不可复制对象分配给内存区域时出现问题。无法以任何方式恢复这样的对象。

这在对象的生命周期中显然很重要,因为它的常量。在它的生命周期结束时,当调用析构函数时,(不存在的)对象简单地是非常量的。

我建议移动析构函数可以成为移动语义模型的一个有价值的补充。

考虑一个简单的情况,其中在unordered_set中使用unique_ptr。您可以使用移动构造函数(或构造“emplace”)insert进入此集合,但是如果您想将此指针移动到另一个unordered_set(即保持const),则无法进行此操作。

必不可少,有iterator insert((possibly const) key&&)但没有const key&& erase(iterator)。事实上,这是不可能的。只能扩展容器以返回指向键的指针,并忘掉它。

移动析构函数可以解决这个问题const MyClass&& ~MyClass(),因为它只会在破坏期间违反const(当编译器认为对象无效时)。

编辑:我应该指出const MyClass&& ~MyClass() const实际上更有意义。析构函数不必修改anyhting,只会破坏对象,就好像它不再是它控制的任何资源的有效句柄。

3 个答案:

答案 0 :(得分:11)

Imho你已经确定了一个真正的需求。

你的解决方案听起来很像我所谓的破坏性移动语义original move semantics proposal中描述了这种可能性。我认为这样的设计是可能的,尽管它并非没有问题。据我所知,没有人在标准委员会这个领域工作。

有一些更简单的方法可以从不需要语言更改的关联容器中提取仅移动类型(除了可能允许没有未定义行为的类型惩罚)。

N3645是一个仅限库的提案,它将嵌套的node_ptr类型放入每个容器中。 node_ptrunique_ptr非常相似。它具有关联容器中节点的唯一所有权。但是,当您取消引用它时,您将获得节点中value_type的非const访问权限,而不是节点本身。 extractinsert成员被添加到关联容器中,允许人们在容器中插入和删除节点(由node_ptr拥有)。

您可以使用它从容器中删除节点,然后将仅移动类型移出节点,并在完成后让~node_ptr()清理节点。本文包含此示例以演示此功能:

set<move_only_type> s;
s.emplace(...);
move_only_type mot = move(*s.extract(s.begin())); // extract, move, deallocate node

请注意s.extractnoexcept,当然~node_ptr()也是如此。如果move_only_type的移动结构为noexcept,则整个操作为noexcept。否则,如果移动构造抛出,set就会被遗留,就像该项目已从set中删除一样。

目前,N3645没有取得任何进展。它还没有被投票到工作草案中,我对它永远不会有信心。

更新C ++ 17

我的观点是纠正的:我上面描述的功能是用P0083R3投票到C ++ 17中的。感谢Cosme在下面的评论中提醒我这一点。

答案 1 :(得分:2)

很抱歉,但前提是有缺陷的。

unordered_set实际上并不包含const个对象。它只是没有给你写入包含元素的权限。这只是访问者的属性。

可以添加一个key erase(iterator)函数,它只是将元素移出一个临时元素。我不确定你为什么要key&&那里。

对于const MyClass&& ~MyClass() const,由于三个原因没有意义:dtors既没有返回类型也没有CV分类,也没有为它们完成重载决策。

答案 2 :(得分:1)

所以基本上你说它应该可以将const对象移动到另一个const对象并销毁原始对象?

很抱歉,但我认为制作const的全部是为了防止这种情况发生。

否则会形成一个漏洞:你可以销毁 - 将const对象移出其内存位置,然后你将另一个 const对象销毁到内存中第一个的位置(通过展示位置new)。

现在该对象已经更改了,即使它是const ...所以基本上const是没用的。

见下面的评论......