使用std :: map :: extract修改密钥

时间:2018-11-07 19:15:07

标签: c++ pointers extract stdmap

我的实现使用std :: map存储数据。当我启动代码时,这似乎是最好的选择。现在,我不得不更改地图中所有对象的键值。

问题在于每个对象都指向地图内的另一个对象:

class AND : public node{
    vector <node*> inputs;
    vector <node*> outputs;
}

地图的声明如下:

map<unsigned int, AND> all_ANDs;

我的问题是:如果我使用C ++ 17中的map :: extract修改all_ANDs映射中的键值,我的指针(例如,属性输入中的指针)将继续指向合适的地方?

换句话说::如果我用extract更改“ first”元素的值,那么“ second”的地址会保持不变吗?

我从this link中注意到,字符串“木瓜”保持不变(并且正常运行)。但是我想确定指针。

3 个答案:

答案 0 :(得分:5)

您已经在帖子中引用的reference明确指出没有元素被复制或移动。 (这假设您的代码段中的node没有引用map::node_type)。

对映射节点的insert操作 (修改其键之后)也是如此:

  

如果插入成功,则将其保留在节点句柄中时获得的指向该元素的指针和引用无效,并且在提取该元素之前获得的指向该元素的指针和引用变为有效。 (自C ++ 17起)

但是,访问extract()和re insert()之间的对象具有未定义的行为,并且在提取状态下获取其地址的用途受到限制。引用标准:

  

提取成员仅使已删除元素的迭代器无效;   指向删除元素的指针和引用仍然有效。然而,   通过这样的指针和引用访问元素,而   元素归node_­type拥有是未定义的行为。参考和   指向由node_­type拥有时获得的元素的指针是   如果成功插入元素,则无效。

说明

本质上,map<>被实现为节点树,每个节点都包含一个keyT(向用户公开为pair<const Key, T>)。节点(通常)在堆上分配,并且对象的地址与节点的地址相关。 map::extract()取消节点与树的链接并返回节点句柄(持有指向地图节点的指针的对象),但是AFAIK节点本身并未重新分配,移动,或复制。在map::insert(handle)上,该节点根据其(新)键重新链接到树中。同样,这不涉及节点的重新分配,移动或复制。

备注

上面是一个粗略的草图。实际操作方式可能更复杂,而且实现也有所定义。如here所述,node_handle确实允许通过成员函数更改键

Key &node_handle::key() const;

没有具体说明如何实现此目的,我推测该实现使用联合或某种强制转换来允许此操作。当然,该地图必须向用户显示pair<const Key,T>,以防止用户更改密钥并因此破坏地图,但这与从地图中提取的元素无关。

答案 1 :(得分:1)

我上面的回答解决了您的直接问题。但是,正如我在评论中建议的那样,它似乎是XY problem。我怀疑:

  1. 您具有AND对象的某种结构,这些对象通过它们的inputsoutputs字段相互链接。此链接不得因任何重新分配而中断,因此您不能将它们存储在不断增长的vector<AND>中并进行重新分配。

  2. 您还希望根据某些key对这些对象进行排序,并因此将它们存储在map<Key,AND>中,该对象实际上在增长时不会重新分配。

  3. 您现在要根据另一个键(和/或更改所有键)对其进行排序。

(如果您实际上对排序不感兴趣,而仅是通过它们的键来查找对象,则应该使用unordered_map代替mapfind()在{ {1}}而不是O(n)个操作。)

我建议您使用其他数据布局:

  1. 您存储O(log(n))对象的方式允许增加它们的数量而无需重新分配。这里一个明显的选择是deque<AND>,因为

      在双端队列两端的

    插入和删除操作永远不会使之无效   指向其余元素的指针或引用

    您还可以将AND设为不可复制且不可移动,以确保一旦分配它们的地址就不会更改(指向它们的指针在销毁之前一直保持有效)。

  2. 通过对AND的向量进行排序或使用{{1}对存储对象的指针进行实​​际处理,可以支持任何按键查找或按键排序操作}}或pair<key,AND*>。您甚至可以同时为每个对象拥有各种键(每个对象都有一个映射)。

  3. 当您必须重新设置所有对象的键时,只需忘记旧的映射并创建一个新的映射:由于该映射仅存储指针而不存储对象,因此这不会影响您的链接。

答案 2 :(得分:0)

您的map保存实际的AND对象,而不是指向对象的指针。因此,如果存储在AND*中的vector指针指向map的{​​{1}}对象,则一旦这些对象为,这些指针将变为无效。从AND中删除

但是,提取只是从map断开指定节点的链接,因此该节点及其键和值在内存中仍然有效。可以将节点重新插入到map中,而不会影响节点的键和值的地址。在这方面,map中的指针将不会变为无效(尽管在将节点从容器中分离时,取消定义它们的引用是未定义的)。

另一种选择是更改您的vector来容纳map指针。或者,最好考虑在AND*中使用std::shared_ptr<AND>中的mapstd::shared_ptr<node>中的vector,而不是原始指针。那么map项是删除还是提取都没关系,只要活动{ {1}}对它们的引用。