简体   繁体   English

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

[英]Using std::map::extract to modify key

My implementation uses std::map to store data. 我的实现使用std :: map来存储数据。 When I started my code it seemed like the best option. 当我启动代码时,这似乎是最好的选择。 Now I came to a point where I have to change the key values of all objects inside the map. 现在,我不得不更改地图中所有对象的键值。

The problem is that each object points to another object inside the map: 问题在于每个对象都指向地图内的另一个对象:

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

And the map is declared like this: 并且地图声明如下:

map<unsigned int, AND> all_ANDs;

My question is: If I use map::extract from C++17 to modify the key values in all_ANDs map, will my pointers (Eg the ones inside the attribute inputs) keep pointing to the right places? 我的问题是:如果我使用C ++ 17中的map :: extract来修改all_ANDs映射中的键值,我的指针(例如,属性输入中的指针)是否将继续指向正确的位置?

In other words: If I change the value of "first" element with extract, the address of "second" will keep intact? 换句话说:如果我用extract更改“ first”元素的值,那么“ second”的地址会保持不变吗?

I noticed from this link that the string "papaya" stays the same (and works gracefully). 我从此链接中注意到,字符串“木瓜”保持不变(并且正常运行)。 But I wanted to be sure about pointers. 但是我想确定指针。

YES The reference you have already quoted in your posts clearly states that no elements are copied or moved . 是是您在帖子中已引用的参考明确指出没有复制或移动元素 (This assumes that node in your code snippet does not refer to map::node_type ). (这假设您的代码段中的node未引用map::node_type )。

The same holds for the insert operation of the map-node (after modifying its key): 映射节点insert操作(修改其键之后)也是如此:

If the insertion is successful, pointers and references to the element obtained while it is held in the node handle are invalidated, and pointers and references obtained to that element before it was extracted become valid. 如果插入成功,则将其保留在节点句柄中时获得的指向该元素的指针和引用无效,并且在提取该元素之前获得的指向该元素的指针和引用变为有效。 (since C++17) (自C ++ 17起)

However, accessing the object between extract() ion and re- insert() ion has undefined behaviour and its address taken whilst in the extracted state is of limited use. 但是,在extract()离子和re- insert()离子之间访问对象具有不确定的行为,并且在提取状态下获取其地址的用途受到限制。 Quoting from the standard: 引用标准:

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. 但是,当元素由node_type拥有时通过此类指针和引用访问元素是未定义的行为。 References and pointers to an element obtained while it is owned by a node_type are invalidated if the element is successfully inserted. 如果成功插入元素,则对由node_type拥有的元素所获得的元素的引用和指针将无效。

Explanation 说明

Essentially, a map<> is implemented as a tree of nodes, each holding a key and T (which are exposed as pair<const Key, T> to the user). 本质上, map<>被实现为节点树,每个节点都拥有一个keyT (向用户显示为pair<const Key, T> )。 Nodes are allocated (typically) on the heap and the address of your object is related to that of a node. 节点(通常)在堆上分配,并且对象的地址与节点的地址相关。 map::extract() un-links a node from its tree and returns a node handle (an object holding a pointer to a map node), but AFAIK the node itself is not re-allocated, moved, or copied. map::extract()取消节点与树的链接,并返回节点句柄 (持有指向地图节点指针的对象),但是AFAIK节点本身并未重新分配,移动或复制。 Upon map::insert(handle) , the node is re-linked into the tree according to its (new) key. map::insert(handle) ,根据节点的(新)键将其重新链接到树中。 Again, this involves no re-allocation, move, or copy of the node. 同样,这不涉及节点的重新分配,移动或复制。

Remark 备注

The above is a rough sketch. 上面是一个粗略的草图。 How things are actually done is likely more complex and also implementation defined. 实际操作方式可能更复杂,而且实现也有所定义。 As explained here a node_handle does allow to alter the key through the member function 如此所述, node_handle确实允许通过成员函数更改密钥

Key &node_handle::key() const;

How this is done under the hood is not specified and I speculate that the implementation uses a union or some cast to allow this. 没有具体说明如何实现此目的,我推测该实现使用联合或某种强制转换来允许此操作。 Of course, the map has to present to users a pair<const Key,T> in order to prevent them from changing the key and hence breaking the map, but this is not of any concern for an element extracted from the map. 当然,该映射必须向用户呈现一pair<const Key,T> ,以防止用户更改密钥并因此破坏映射,但这与从映射中提取的元素无关。

My above answer addresses your immediate question. 我上面的答案解决了您的直接问题。 However, as I have suggested in a comment, this appears to be a XY problem . 但是,正如我在评论中建议的那样,这似乎是XY问题 What I suspect: 我怀疑:

  1. You have some structure of AND objects which are interlinked via their inputs and outputs fields. 您具有一些AND对象的结构AND对象通过其inputsoutputs字段相互链接。 This linkage must not be broken by any re-allocation, so you cannot store them in a growing vector<AND> with re-allocation. 此链接不得因任何重新分配而中断,因此您不能将它们存储在具有重新分配的增长vector<AND>

  2. You also want to order these objects according to some key and have therefore stored them in a map<Key,AND> , which indeed does not re-allocate when grown. 您还希望根据某个key对这些对象进行排序,并因此将它们存储在map<Key,AND> ,该对象实际上在增长时不会重新分配。

  3. You now want to order them according to another key (and/or change all the keys). 现在,您要根据另一个键来订购它们(和/或更改所有键)。

(If you're actually are not interested in ordering but merely in finding your objects by their key, you should have used unordered_map instead of map , which supports find() in O(n) rather than O(log(n)) operations.) (如果您实际上对排序不感兴趣,而仅是通过它们的键来查找对象,则应该使用unordered_map而不是map ,它支持O(n)而不是O(log(n))操作中的find() 。)

I suggest a different layout of your data: 我建议您使用不同的数据布局:

  1. You store your AND objects in a way that allows growing their number without re-allocation. 您以一种无需增加即可增加其数量的方式存储AND对象。 An obvious choice here is deque<AND> , since 一个明显的选择是deque<AND> ,因为

    insertion and deletion at either end of a deque never invalidates pointers or references to the rest of the elements 在双端队列的两端插入和删除绝不会使指向其余元素的指针或引用无效

    You may also make AND non-copyable and non-movable, ensuring that once allocated their address never changes (and pointers to them remain valid until destruction). 您还可以将AND设为不可复制且不可移动,并确保分配后的地址永不更改(指向它们的指针在销毁之前一直保持有效)。

  2. You can support any find-by-key or order-by-key operations by actually working on pointers to the stored objects, either by sorting a vector of pair<key,AND*> or by using a map<key,AND*> or unordered_map<key,AND*> . 您可以通过实际处理指向存储对象的指针来支持任何按键查找或按键排序操作,方法是pair<key,AND*>的向量进行排序pair<key,AND*>或者使用map<key,AND*>unordered_map<key,AND*> You can even simultaneously have various keys per object (and a map for each). 您甚至可以同时为每个对象具有不同的键(每个对象都有一个映射)。

  3. When you must re-key all objects, simply forget the old map and make a new one: since the map only stores pointers and not the objects, this does not affect your linkages. 当您必须重新设置所有对象的键时,只需忘记旧的映射并创建一个新的映射:由于该映射仅存储指针而不存储对象,因此这不会影响您的链接。

Your map holds actual AND objects, not pointers to objects. 您的map包含实际的AND对象,而不是指向对象的指针。 So, if the AND* pointers stored inside your vector s are pointing at the map 's AND objects, then those pointers WILL become invalid once those objects are erased from the map . 因此,如果vector存储的AND*指针指向mapAND对象,则一旦将这些对象从map删除 ,这些指针将变为无效。

However, extraction merely unlinks a specified node from the map , the node and thus its key and value are still valid in memory. 但是, 提取仅会使map的指定节点断开链接,因此该节点及其键和值在内存中仍然有效。 The node can be re-inserted into a map without affecting the addresses of the node's key and value. 可以将节点重新插入到map而不会影响节点的键和值的地址。 In this regard, the pointers in the vector s WILL NOT become invalid (although it is undefined to dereference them while the node is detached from the container). 就这一点而言, vector s中的指针将不会变为无效(尽管在将节点从容器中分离时未定义取消对它们的引用)。

Another option is to change your map to hold AND* pointers instead. 另一个选择是更改您的map以保留AND*指针。 Or better, consider using std::shared_ptr<AND> in the map and std::shared_ptr<node> in the vector s, instead of raw pointers. 或者更好的方法是考虑在map使用std::shared_ptr<AND> ,在vector s中使用std::shared_ptr<node> ,而不是原始指针。 Then it won't matter whether the map entries are erased or extracted , the AND objects will remain valid as long as there are active shared_ptr references to them. 然后,无论是删除还是提取 map条目都没有关系,只要有活动的shared_ptr引用, AND对象将保持有效。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM