§23.1.2.8 in the standard states that insertion/deletion operations on a set/map will not invalidate any iterators to those objects (except iterators pointing to a deleted eleme
I had a similar question recently, but I was wondering if calling end()
to retrieve an iterator for comparison purposes could possibly have race conditions.
According to the standard, two iterators are considered equivalent if both can be dereferenced and &*a == &*b
or if neither can be dereferenced. Finding the bolded statement took a while and is very relevant here.
Because an std::map::iterator
cannot be invalidated unless the element it points to has been removed, you're guaranteed that two iterators returned by end
, regardless of what the state of the map was when they were obtained, will always compare to each other as true.
Assuming that (1) map implemented with red-black tree (2) you use same instance "after a zillion insertions/deletions"- answer "Yes".
Relative implmentation I can tell that all incarnation of stl I ever know use the tree algorithm.
C++ Standard states that iterators should stay valid. And it is. Standard clearly states that in 23.1.2/8:
The insert 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.
And in 21.1/7:
end() returns an iterator which is the past-the-end value for the container.
So iterators old_end
and new_end
will be valid. That means that we could get --old_end
(call it it1
) and --new_end
(call it it2
) and it will be the-end value iterators (from definition of what end()
returns), since iterator of an associative container is of the bidirectional iterator category (according to 23.1.2/6) and according to definition of --r
operation (Table 75).
Now it1
should be equal it2
since it gives the-end value, which is only one (23.1.2/9). Then from 24.1.3 follows that: The condition that a == b implies ++a == ++b. And ++it1
and ++it2
will give old_end
and new_end
iterators (from definition of ++r operation Table 74). Now we get that old_end
and new_end
should be equal.
Well, there's nothing preventing particular collection implementation from having end()
depend on the instance of collection and time of day, as long as comparisons and such work. Which means, that, perhaps, end()
value may change, but old_end == end()
comparison should still yield true. (edit: although after reading the comment from j_random_hacker, I doubt this paragraph itself evaluates to true ;-), not universally — see the discussion below )
I also doubt you can use std::map<int,Node>::iterator
in the Node
class due to the type being incomplete, yet (not sure, though).
Also, since your nodes are uniquely numbered, you can use int
for keying them and reserve some value for invalid.
Iterators in (multi)sets and (multi)maps won't be invalidated in insertions and deletions and thus comparing .end() against previous stored values of .end() will always yield true.
Take as an example GNU libstdc++ implementation where .end() in maps returns the default intialized value of Rb_tree_node
From stl_tree.h:
_M_initialize()
{
this->_M_header._M_color = _S_red;
this->_M_header._M_parent = 0;
this->_M_header._M_left = &this->_M_header;
this->_M_header._M_right = &this->_M_header;
}
23.1/7 says that end() returns an iterator that
is the past-the-end value for the container.
First, it confirms that what end()
returns is the iterator. Second, it says that the iterator doesn't point to a particular element. Since deletion can only invalidate iterators that point somewhere (to the element being deleted), deletions can't invalidate end()
.