Continuing to iterate through std::map after insertion - c++

This question clarified that iterators remain valid after insertion. I'd like to go a bit further and ask for verification that this is expected behavior:
std::map automatically sorts based on key.
insertion into the map thus automatically places the new element in a sorted location somewhere inside the map
An iterator++ operation therefore could return a different element after insertion than it would have prior to insertion.
Does this all make sense?

this's true if the new inserted element is after the iterator you are visiting.

Related

C++11: does unordered_map/set guarantees traversing order as insert order?

I wrote some code like this:
unordered_map<int, int> uii;
uii.insert(make_pair(12,4));
uii.insert(make_pair(3,2));
uii.insert(make_pair(6,1));
uii.insert(make_pair(16,9));
....
When I use a for loop to visit this map, it prints key in the right order of my insertion. I tested unordered_set, with same result.
So my question is, does the C++ standard guarantee the visiting order as insert order, just like Java's LinkedHashMap?
No, it is unordered, there is no such guarantee.
Elements in an unordered associative container are organized into
buckets, keys with the same hash will end up in the same bucket. The
number of buckets is increased when the size of the container
increases to keep the average number of elements in each bucket under
a certain value.
Rehashing invalidates iterator and might cause the elements to be
re-arranged in different buckets but it doesn't invalidate references
to the elements.
This is valid for both unordered_map and unordered_set.
You might also want to check this question Keep the order of unordered_map as we insert a new key
But, internally an implementation of unordered container might use list or other ordered container to store elements and store only references to sublists in its buckets, that would make iteration order to coincide with the insertion order until enough elements are inserted to cause list rearranging. That is the case with VS implementation.

Efficiently check element exists in map c++

I am aware of the .find() method to check whether or not an element exists in a map. But suppose i have a map -- map -- with about 2000 elements and I want to add an element to the list. I first have to check if the element exists. Isn't it a bit inefficient to use .find() because the iterator has to 'iterate' over element in the map? Is there a more efficient and less time consuming way to check whether or not an element exists in a map?
std::map has logarithmic look-up complexity, so you won't have to visit all the elements to determine whether an element is in the map.
On the other hand, you can make the look-up and insert operation more concise by using std::map::emplace or std::map::insert:
auto [iter, ok] = the_map.emplace(the_key, a_value);
if (ok) {
// element was not in map so got inserted
}
This assumes that a_value either already exists, or that it is not an issue to construct it even if it does not end up being inserted into the map.
If you really need to use 'find and insert' idiom (and that could be because you can't construct a value object for the same key twice under any circumstances) and you are concerned about twice logarithmic complexity, you'd have to use std::map::equal_range.
The way to do this would be first use equal_range with your key, and than use one of returned iterators as a hint to insert.

Why doesn't `std::forward_list::insert_after` return the first element inserted as other sequence containers?

Why doesn't std::forward_list::insert_after return the first inserted element as other sequence containers such as list and vector. Are there any deliberate reasons?
forward_list is very different from other sequences, as is insert_after. In order to return the first inserted item it would have to use extra time and space to save off that element, while the last element will be available as part of the insertion algorithm. Not only that, but returning an iterator to the first element inserted from a range would give you an iterator you could use to insert into the middle of the range you just added, while an iterator to the end of the range lets you append additional data.
I don't know the committee's reasoning, but here's mine:
You can insert multiple elements, e.g. a "range". See http://en.cppreference.com/w/cpp/container/forward_list/insert_after
Returning the first element wouldn't be really valuable, as getting to the first element is trivial - you already have an iterator after which it was inserted.
OTOH, getting to the last inserted element might be costly, if you've inserted a lot of nodes.
After considering for some time, here is my understanding.
1. Normal sequence containers
For normal sequence containers, insert gets an iterator (which I'll call itl) as parameter, and inserts elements before itl. Then returns the iterator (itf) that points to the first inserted element. Now you have 2 iterators separately denoting the range (first and off-the-end) of the inserted elements.
elem-elem-inserted-inserted-inserted-elem-elem
| |
(itf) (itl)
However, insert operation may invalidate itl, so we need to consider the following two situations:
1.1 Linked structure
For linked structure, itl remains valid. Therefore (itf,itl) forms a valid range for inserted elements.
1.2 Contiguous structure
For contiguous structure, itl are invalidated after insertion. However such structures usually support random access, so it's still relatively easy to get an itl through simple arithmetic on itf. On the other hand, returning an iterator to the first inserted element preserves consistency.
2. Forward-list
insert_after for forward-list gets an iterator (itf) as parameter, and inserts elements after it. Then it returns an iterator to the last inserted element (itl). Because itf remains valid, we have a similar range again.
elem-elem-inserted-inserted-inserted-elem-elem
| |
(itf) (itl)
3. Conclusion
No matter whether insert operation returns a iterator to the first inserted element or to the last inserted element, it always tries to provide access to the beginning and end of inserted elements at the same time, which provides maximum flexibility.

Peculiar behaviour while erasing an element from from std::multimap

I was trying to remove an element from a std::multimap while I am looping over it in a thread that manipulates it . I used erase function in the following ways
When I do this
//mItr is base iterator which loops over the multimap
std::multimap<std::string, std::string>::iterator tmpItr = ++mItr;
healthyQ.erase(mItr);
mItr = tmpItr;
so that I can have validated iterator after erasing the element from multimap, the program stalls in erase() call . So I used it in the following way to get the next valid iterator :
mItr = healthyQ.erase(mItr);
It worked. It consumed a lot of time and I am still not sure where the problem can be
The second way is exactly how it is supposed to work
When you hold an iterator to a tree-based container and you erase it, it alters the pointers between the various nodes pointing to this node (and others). Even if you would know exactly what this node is (through the iterator), you are left with no indication what is the next node (and consequently, the next iterator). Because of this, the erase method first finds the next node, performs the erase, and then returns an iterator to this next node.
You can see here how removal works in a red-black tree.
You invalidated the iterator by calling erase() function. So when you capture the iterator returns from erase() and reuse later you are properly handling the iterator and avoiding iterator invalidation.

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.