I'm thinking of writing some code which will boil down to this. T
is a collection type (currently std::map
, if it matters).
T coll;
// ...
T::iterator it, prev;
prev = coll.end();
for(it = coll.begin(); it != coll.end(); ++it) {
if(prev != coll.end())
{ do something involving previous element; }
do something involving this element;
prev = it;
}
My question is, is it in any way a bad idea to copy it
to prev
like this? Bad style? Likely to annoy a pedant somewhere? Possible to break depending on subtle details of type T
?
I don't expect coll
to be destroyed while this loop is running, or for any elements to be added to or deleted from it.
On the one hand everything I know about iterators says that this should be Perfectly Safe. But on the other hand, there are things I don't know about iterators, and this feels ever-so-slightly sketchy, so I thought I'd ask.
Addendum:
The other reason this comes up is that my actual code isn't going to involve the for
loop I wrote above. What I actually have is event-driven code; which would look more like this:
void onStartDoingSomething() {
it = coll.start();
prev = coll.end();
startOperation(it, prev);
timerStart();
}
void onTimer() {
if(finished with operation on it) {
prev = it;
++it;
startOperation(it, prev);
if(it == coll.end() {
timerStop();
call operation finished;
}
}
}
void startOperation(T::iterator next, T::iterator prev) {
if(prev != coll.end()) {
finish operation on prev;
}
if(next != coll.end()) {
start operation on next;
}
}
So another way of stating my question might be, "Do you have to use iterators, and a collection class's begin()
and end()
methods, only in straitlaced conventional for
loops, or can you use them arbitrarily? Is there any state kept in the for
loop, or is the state all in the iterator?" Now, I know that there's nothing like "state stored in the for
loop", and as far as I know, it's paramount for an iterator to contain all the state it needs. So if, in fact, the iterator not only contains all the state it needs, but is also 100% safely copyable, then the answer to my original question is "No, it's not a bad idea". But the lurking possibility that iterators might not be 100% safely copyable -- for example, if there were subtle aliasing problems if you copied an iterator, incremented the original, then tried to use the copy -- is why this code feels ever-so-slightly sketchy to me.
It depends on the type of iterator, but it's fine for std::map::iterator
.
Iterators fall into different categories, depending on the operations they support and the guarantees they provide about the value they refer to.
std::map::iterator
satisfies the requirements for the BidirectionalIterator
concept. That means that, among other things, incrementing a copy of an iterator will not invalidate the original. That means that doing prev = it; ++it
will not invalidate prev
, so your algorithm is well-defined.
This is not the case for all iterators, however. The InputIterator
concept does not provide this guarantee. Note the postcondition for ++i
:
Postcondition: Any copies of the previous value of
i
are no longer required to be either dereferenceable or to be in the domain of==
.
I'm not aware of any iterators in the standard library that will lose their value if you increment a copy of them, but I have personally built just such an iterator type (it iterates over entries in an archive file, and the previous entry is no longer readable when you advance to the next one).
See also the Iterator
, ForwardIterator
, RandomAccessIterator
, and OutputIterator
concepts. All of them build on each other.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With