c++ - 为什么取消引用指向 std::set 的提取节点的指针是未定义的行为?

标签 c++ pointers stl c++17 stdset

我在我的公司做了一个关于关联容器新的(C++17)拼接接口(interface)的闪电演讲。我演示了 std::set::extract然后被问到迭代器和指向提取元素的指针会发生什么。他们抓错了我的脚,我无法回答这个问题,但在谈话结束后立即查了一下。

[associative.reqmts] 21.2.6.10在目前的标准草案中是这样写的:

The extract members invalidate only iterators to the removed element; pointers and references to the removed element remain valid. However, accessing the element through such pointers and references while the element is owned by a node_­type is undefined behavior. References and pointers to an element obtained while it is owned by a node_­type are invalidated if the element is successfully inserted.

(提案P0083R3已经包含了这个字眼)

现在强调的部分真的让我很不安。我了解有效但不可取消引用的指针 (nullptr) 或迭代器(结束迭代器)的概念。我找到了一个 "definition" of valid pointers通过 David Vandevoorde 了解到还有一些有效但不可取消引用的指针不是 nullptr。 (即指向现有对象的指针)

有了这一切,我对发生的事情的心智模型如下:

  1. 检索到指向集合中某个元素的指针,因此该指针是有效的。取消引用该指针已定义并产生对集合中元素的访问。
  2. 现在从集合中提取相同的元素。从概念上讲,元素不会被复制、移动或以其他方式更改,它的关联节点只是从 set 管理其数据的内部树中删除。剩余的树可能需要重新平衡。返回的 node_handle 拥有孤立树节点的所有权。

按照标准,在 1) 中检索到的指针仍然有效并且不能被 extract 更改,因此这也支持这种心智模型。然而,对于这个模型,没有理由取消引用指针会突然未定义。因此在 coliru 上使用 g++ seems to work as I would have expected . (这不是任何形式的证据)

标准为库实现者提供的余地似乎不必要地大。我错过了什么?我只看到在提取它们时设置值的常量性被丢弃,但看不到这会有什么影响。

同样的推理适用于最后引用的句子中提到的插入案例。

最佳答案

您的心智模型忽略了这样一个事实,即删除常量性,严格来说,必须是实现定义的。

node_handle必须取得一个不同对象的所有权,但通过一些实现定义的魔法,该可变对象在没有被构造的情况下突然出现,具有与原始 const 对象相同的值和存储。

类似地,当它被插入到一个具有兼容分配器的集合中时,该集合会转换 node_handle 拥有的可变对象。回到原来的 const 对象。

这是未定义的行为,因为 const 对象已不复存在,而“它”属于 node_handle。 , 但当它被重新插入时它又开始存在。

使用 node_handle 是未定义行为的同一种推理。如果您具有 std::pair<const K, V> 的用户定义特化,则来自 map 或 std::pair<K, V> .您不想将实现限制在实现所有这些的“魔术”方式上,因此您可以制作任何可以观察到“魔术”未定义行为的东西。

关于c++ - 为什么取消引用指向 std::set 的提取节点的指针是未定义的行为?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/54925496/

相关文章:

c++ - 成员变量作为指向模板类型的指针 C++

visual-studio-2010 - 在 VS2010 上使用 STL 的多线程 Win32 服务的内存问题

c++ STL sort 带有额外的参数 'invalid operator <'

c++ - 用c++读写

c++ - 指针分配与正常声明

c++ - vs2015 :vc++ for Linux Development no such file

C - SizeOf 指针

C++ Bimap 左 unordered_map 右排序可变 multimap

c++ - 一个指针,c++中的两个不同的类

c++ - 为什么函数 calcOpticalFlowPyrLK 返回相同的值?