Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Is it a bad idea to make a copy of an iterator?

Tags:

c++

iterator

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.

like image 591
Steve Summit Avatar asked Mar 06 '23 07:03

Steve Summit


1 Answers

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.

like image 112
Miles Budnek Avatar answered Mar 15 '23 14:03

Miles Budnek