Do STL iterators guarantee validity after collection was changed? - c++

Let's say I have some kind of collection and I obtained an iterator for the beginning of it. Now let's say I modified the collection. Can I still use the iterator safely, regardless of the type of the collection or the iterator?
To avoid confusion, here is the order of operations I talk about:
Get an iterator of the collection.
Modify the collection (obviously
not an element in it, but the collection itself).
Use the iterator obtained at step 1. Is it stil valid according to STL standard?!

Depends on the container. e.g. if it's a vector, after modifying the container all iterators can be invalidated. However, if it's a list, the iterators irrelevant to the modified place will remain valid.
A vector's iterators are invalidated when its memory is reallocated. Additionally, inserting or deleting an element in the middle of a vector invalidates all iterators that point to elements following the insertion or deletion point. It follows that you can prevent a vector's iterators from being invalidated if you use reserve() to preallocate as much memory as the vector will ever use, and if all insertions and deletions are at the vector's end. [1]
The semantics of iterator invalidation for deque is as follows. Insert (including push_front and push_back) invalidates all iterators that refer to a deque. Erase in the middle of a deque invalidates all iterators that refer to the deque. Erase at the beginning or end of a deque (including pop_front and pop_back) invalidates an iterator only if it points to the erased element. [2]
Lists have the important property that insertion and splicing do not invalidate iterators to list elements, and that even removal invalidates only the iterators that point to the elements that are removed. [3]
Map has the important property that inserting a new element into a map does not invalidate iterators that point to existing elements. Erasing an element from a map also does not invalidate any iterators, except, of course, for iterators that actually point to the element that is being erased. [4] (same for set, multiset and multimap)

That depends on the collection in question. Just for example, modifying a std::vector (e.g., adding an element somewhere) can invalidate all iterators into that vector. By contrast, with a std::list, iterators remain valid when you add another element to the list. In some cases, the rules are even more complex (e.g., if memory serves, with a std::deque, adding to the beginning or end leaves existing iterators valid, but adding anywhere else can invalidate them -- but my memory is sufficiently poor that you should check before depending on that).

No, iterators are only good while the iterated container is unchanged. If a collection is modified, the iterator should be obtained anew.

Related

Will std::deque preserve pointer validity of it's contained objects?

I'm looking for a container that preserves it's contained objects' positions in memory (it's pointers remain valid)
The container will grow and shrink constantly. Elements in the middle may be erased, but there's no insertions in the middle; all elements are pushed onto the back of the container. Iterator validity isn't important in this case, my only concern is that the pointers remain valid.
Is std::deque a safe and efficient option in this situation? I was previously using list, but it is allocating far too many times to be useful in this instance.
No. std::deque is necessarily implemented using chunks. Erasing in the middle of a chunk would at the very least invalidate the addresses of all subsequent elements in that chunk.
Note that iterator invalidation and pointer invalidation are generally closely connected. An iterator often is a pointer to the (node holding the) element, with proper iteration semantics added. Such iterators get invalidated because the pointer they contain is invalidated.
Inserting or removing elements in the middle invalidates all iterators and references.
Inserting elements at the beginning/end invalidates iterators, but does not affect references.
Removing elements at the beginning/end does not affect iterators or references, except ones pointing to the removed element, and possibly the past-the-end iterator.
http://en.cppreference.com/w/cpp/container/deque/erase
All iterators and references are invalidated, unless the erased elements are at the end or the beginning of the container, in which case only the iterators and references to the erased elements are invalidated.
http://en.cppreference.com/w/cpp/container/deque/push_back
All iterators, including the past-the-end iterator, are invalidated. No references are invalidated.
(other methods that operate on front or back elements have similar notes).
NO ! As opposed to std::vector, the elements of a deque are not stored contiguously: typical implementations use a sequence of individually allocated fixed-size arrays.
The storage of a deque is automatically expanded and contracted as needed. Expansion of a deque is cheaper than the expansion of a std::vector because it does not involve copying of the existing elements to a new memory location

For which standard container, if any, is the iterator returned by end() persistent?

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.
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.
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
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.

When ptr_vector iterators are invalidated

Is it possible that inserting and/or erasing elements can invalidate iterators to existing elements.
Thank you.
Yes. The documentation for boost::ptr_vector<T> states:
A ptr_vector<T> is a pointer container that uses an underlying std::vector<void*> to store the pointers.
And inserting elements into or erasing elements from a std::vector can cause re-allocation and therefore existing iterators to be invalidated.
Specifically, §23.3.6.5/3 of C++11 states about erase():
(3) Effects: Invalidates iterators and references at or after the point of the erase.
and about insert() and push_back():
(1) Remarks: Causes reallocation if the new size is greater than the old capacity. If no reallocation happens, all the iterators and references before the insertion point remain valid.
Therefore, to prevent iterator invalidation in the case of element insertion, you may use the reserve() function to increase the vector's capacity before obtaining any iterators from it. Those iterators will then remain valid until the size() of the vector exceeds the number of elements space was reserved for.

Deque erase from middle, is pointer lost?

i erase elements from middle of my deque frequently. memory address of elements in deque are used in some other places. does erasing middle element from deque invalided all pointers to deque like it does with vectors? should i switch to list?(i iterate whole deque anyways)
i tried to read deque implementation but it is complex, i don't understand if it acts as a list or vector.
I'm using visualc++ std implementation of deque.
Yes. An insertion in the middle invalidates all iterators and references to elements (and thus pointers to elements). An insertion at either end invalidates all iterators, but not references or pointers. And you don't read the implementation to find out such things; you read the documentation. (The implementation may actually allow operations which aren't officially supported. Until the next bug fix.)
does erasing middle element from deque invalided all pointers to deque like it does with vectors?
In case of a deque all iterators and references to the deque are invalidated, unless the erased members are at an end (front or back) of the deque.
For a vector all iterators and references before the point of insertion are unaffected, unless the new container size is greater than the previous capacity.

C++: how to track a pointer to a STL list element?

I would like to track a pointer to a list element for the next read access. This pointer would be advanced every time the list is read. Would it be bad practice to cache an iterator to the list as a member variable, assuming I take proper precautions when deleting list elements?
An iterator to a list element remains valid until the element it refers to is removed. This is guaranteed by the standard.
There is no problem with caching an iterator as long as you make sure you don't remove elements from the list or refresh the cached iterator when you do.
Iterators are meant to be used. They aren't just for looping over each element in a for-loop. All you have to take into account is the rules for iterator invalidation.
std::vector iterators can be invalidated by any operation that inserts elements into the list. The iterators for all elements beyond the point of insertion are invalidated, and all iterators are invalidated if the insertion operation causes an increase in capacity. An operation that removes an element from the vector will invalidate any iterator after the point of removal.
std::deque iterators are invalidated by any operation that adds or removes elements from anywhere in the deque. So it's probably not a good idea to keep these around very long.
std::list, std::set, and std::map iterators are only invalidated by the specific removal of the particular element that the iterator refers to. These are the longest-lived of the iterator types.
As long as you keep these rules in mind, feel free to store these iterators all you want. It certainly isn't bad form to store std::list iterators, as long as you can be sure that that particular element isn't going anywhere.
The only way you're going to be able to properly advance though an STL std::list<T> in a platform and compiler independent way is through the use of a std::list<T>::iterator or std::list<T>::const_iterator, so that's really your only option unless you're planning on implementing your own linked-list. Per the standard, as others here have posted, an iterator to a std::list element will remain valid until that element is removed from the list.