问题
I need a way to quickly access data in a container.
So I remember iterator of that data position. Container maybe modified (elements added and removed) after that, but if I use container type that does not invalidate my iterator (like std::map or std::list) I am fine.
Also my data may not be in the container (yet), so I set an iterator to container.end() to reflect that.
Which standard container guarantees that end() would not change when elements added and removed? So I can still compare my iterator to the value returned by container.end() and not get false negative.
回答1:
23.2.4/9 says of Associative Containers:
The insert and emplace members shall not affect the validity of iterators and references to the container, and the erase members shall invalidate only iterators and references to the erased elements
Now, there are some places where the standard talks about not invalidating "iterators and references to elements of the container", thus excluding end(). I don't believe that this is one of them - I'm pretty sure that an end() iterator is an "iterator to the container".
23.3.5.4/1 says for std::list that insert "Does not affect the validity of iterators and references", and 23.3.5.4/3 says that erase "invalidates only the iterators and references to the erased elements". Again, end() iterators are iterators and so their validity isn't excluded.
One thing to watch out for is that for any container, swap can invalidate end() iterators (I assume this is because there are two "natural" behaviors, either that the end iterator points to the end of the same container or else to the end of the one it was swapped with, but the standard doesn't want to dictate which or rule out other possibilities). But you aren't swapping, just adding and removing elements.
回答2:
From my experience, iterators from std::vector and std::dequeue break upon resizing from erasure or addition (this applies to std::string's storage as well). std::list and std::map don't allocate blocks of memory: they usually allocate individual nodes (and most std::unordered_map implementations have buckets as a linked list of items (e.g., std::list)).
If you need to have a surviving end() iterator, choose a std::list (I do this for my Signal/Slots implementation, for their tokens) or do your own personal bookkeeping with std::vector/std::dequeue.
EDIT:
So, std::list is a good way to have your iterators be always valid, provided your list itself never dies (which they aren't). From another answer, if you need standardese clarity:
23.3.5.4/1 says for std::list that insert "Does not affect the validity of iterators and references", and 23.3.5.4/3 says that erase "invalidates only the iterators and references to the erased elements". Again, end() iterators are iterators and so their validity isn't excluded. - Another Answer
回答3:
Instead of iterators, use a vector and store index values. They'll survive any restructuring. Iterators are primarily intended for use in pairs that designate a range; hanging on to individual iterators gets messy, as you've seen.
来源:https://stackoverflow.com/questions/15414541/for-which-standard-container-if-any-is-the-iterator-returned-by-end-persiste