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.
Related
I'm new to C++ and want to avoid newbie performance pitfalls.
Can erasing elements from a vector possibly cause a memory reallocation?
I'm working on a function that will add elements to a vector, and possibly erase some elements in the same frame.
No, it does not. If it did, it would invalidate all iterators, but, as cppreference states, it only Invalidates iterators and references at or after the point of the erase, including the end() iterator.
Is it guaranteed that std::vector only moves its data when size()==capacity() and calling push_back() or emplace_back() or can it do it otherwise also?
The specification is a bit indirect. capacity is specified as:
size_type capacity() const noexcept;
Returns: The total number of elements that the vector can hold without requiring reallocation.
The second part comes from reserve:
reserve(size_type n);
Remarks: Reallocation invalidates all the references, pointers, and iterators referring to the elements in
the sequence. No reallocation shall take place during insertions that happen after a call to reserve()
until the time when an insertion would make the size of the vector greater than the value of capacity().
From that you can conclude that if the size is less than the capacity, then insertion does not cause reallocation.
There is no single, direct statement that the vector will not reallocate if there is spare capacity and you haven't explicitly called reserve. However, there is a general container requirement in [container.requirements.general]:
Unless otherwise specified (either explicitly or by defining a function in terms of other functions), invoking a container member function or passing a container as an argument to a library function shall not invalidate iterators to, or change the values of, objects within that container.
Finally, we have description of the effects of insertion:
[insert/emplace_back/push_back:]
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.
Putting it all together: Unless otherwise specified, calling a member function does not invalidate iterators. Reallocation invalidates iterators (as described as part of reserve above), so unless otherwise specified, calling a member function, and in particular an insertion, does not reallocate, One such an overriding specification is given for the case where the new size exceeds the current capacity.
The description of standard is not clear enough.
$23.3.6.5 vector modifiers [vector.modifiers]:
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.
So when add elements to std::vector reallocation will certainly happen when the new size is greater than current capacity, but it doesn't says reallocation won't happen even if the new size is less than or equal to current capacity. Anyway if reallocation doesn't happen then all the iterators and references to the elements before the insertion point must remain valid, implies data won't be moved.
It's the same for insert(), emplace_back(), emplace() and push_back().
Quote from cppreference.com just as reference:
If the new size() is greater than capacity() then all iterators and
references (including the past-the-end iterator) are invalidated.
Otherwise only the past-the-end iterator is invalidated.
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
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.
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.