views:

97

answers:

1

Most of the time, STL iterators are CopyConstructable, because several STL algorithms require this to improve performance, such as std::sort.

However, I've been working on a pet project to wrap the FindXFile API (previously asked about), but the problem is it's impossible to implement a copyable iterator around this API. A find handle cannot be duplicated by any means -- DuplicateHandle specifically forbids passing these types of handles to it. And if you just maintain a reference count to the find handle, then a single increment by any copy results in an increment of all copies -- clearly that is not what a copy constructed iterator is supposed to do.

Since I can't satisfy the traditional copy constructible requirement for iterators here, is it even worth trying to create an "STL style" iterator? On one hand, creating some other enumeration method is going to not fall into normal STL conventions, but on the other, following STL conventions are going to confuse users of this iterator if they try to CopyConstruct it later.

Which is the lesser of two evils?

+3  A: 

An input iterator which is not a forward iterator is copyable, but you can only "use" one of the copies: incrementing any of them invalidates the others (dereferencing one of them does not invalidate the others). This allows it to be passed to algorithms, but the algorithm must complete with a single pass. You can tell which algorithms are OK by checking their requirements - for example copy requires only an InputIterator, whereas adjacent_find requires a ForwardIterator (first one I found).

It sounds to me as though this describes your situation. Just copy the handle (or a pointer to something which has the handle), without duplicating it.

The user has to understand that it's only an InputIterator, but in practice this isn't a big deal. istream_iterator is the same, and for the same reason.

Steve Jessop
I don't think that copying an input iterator _invalidates_ (not in the sense of that word in the standard) the original iterator. It does have side effects (that is, the result of `*it1` might be different if called before or after `++it2`) but none of the copies AFAIK are invalidated. Table 72 in the standard, in §24.1.1/2 defines `a = u` as a valid operation and the post-conditions don't mention any invalidation., §24.1.1/3 deals with specific behavior only present in input iterators: `a == b` does not imply `++a == ++b` (i.e. only valid in single-pass algorithms)
David Rodríguez - dribeas
To keep the semantics of an input iterator, the handle must be shared among the different instances of the copied iterators, and you must ensure that the handle is released at the appropriate time and no earlier/later.
David Rodríguez - dribeas
@David: Yes. Usually I do this by using the pimpl idiom with a noncopyable inner class and `boost::shared_ptr`.
Billy ONeal
@dribeas: correct. As I stated, copying an input iterator is allowed. In 24.1.1, Table 24 states the post condition for `operator++`: "Any copies of the previous value of `r` are no longer required either to be dereferenceable or to be in the domain of `==`". I think "invalidates" is a reasonable summary of that, although it doesn't actually say that you can't increment a copy of the previous value and then dereference *that*. It does say (1) that it doesn't necessarily give the same result as the one you incremented first, and (2) that you "should" never do it.
Steve Jessop