Home > Software engineering >  Standard-conforming transform_iterator
Standard-conforming transform_iterator

Time:01-15

My goal would be to have an iterator that iterates over elements of type T, but in my case T is not really usable for my end-users. Instead, end-users should use a wrapper W that has a much more useful interface.

In order to construct a W, we need a T plus a reference or pointer to an additional data structure.

The problem is that I will never store elements as W. Instead, elements are always stored as T and only wrapped on-demand. Therefore, my users have to iterator over a data structure holding Ts.

My idea was to write a custom iterator for these data structures that itself iterates over the stored Ts, but upon dereferencing will return W instead. I started looking into how this can be implemented and found various information on this topic, including how to deal with the iterator's reference typedef not actually being a reference. This includes the arrow_proxy trick for implementing operator-> in such cases.

However, I have also attempted to read in the standard to see what it has to say about such iterators. My resource here is this and there it clearly states that as soon as we are dealing with forward iterators, reference is expected to be a (const) reference to the value_type. This is supported by this question.

This makes me wonder whether it is even possible to reasonably implement such a transform_iterator that remains standard conforming if one intends to be using it as a forward_iterator or above?

One way that I could come up with would be to declare the value_type of my iterator to W and then keep a member variable of type W around such that operator* could be implemented like this:

class transform_iterator {
    value_type = W;
    reference = W &;
    // ...

    reference operator*() const {
        m_wrapper = W(< obtain current T >, m_struct);
        return m_wrapper;
    }

    mutable W m_wrapper;
    SeparateDataStructure m_truct;
};

However, this approach seems rather hacky to me. On top of that would this increase the iterators size seemingly considerably, which might or might not be an issue (in the long run).


Note 1: I know that Boost.iterator provides a transform_iterator, but I can't quite follow through the implementation of what iterator category they actually apply to these types of iterators. However, it does seem like they base the category on the result type of the supplied function (in some way), which would suggest that at least in their implementation it is possible for the category to be different from input_iterator_tag (though maybe the only other option is output_iterator_tag?).

Note 2: The question linked above also suggest the same workaround that I sketched here. Does that indicate that there is no better way?


TL;DR: Is there a better way to achieve e.g. a forward iterator that transforms the iterated type on dereference to a different type than to store a member of that type in the iterator itself and update that member on every dereference?

CodePudding user response:

First, choose the most restrictive iterator category you can get away with:

  • if you don't need to allow multiple passes, use InputIterator and just return a temporary W by value

    This still has all the usual iterator methods (operator*, operator->, both operator etc.)

  • if you do need multiple passes, you need a ForwardIterator with its additional requirement to return an actual reference.

    As you say, this can only be done by storing a W somewhere (in the iterator or off to the side).

    The big problem is with mutable forward iterators: mutating *i must also affect *j if i == j, and that means your W can't be a standalone value type, but must be some kind of write-through proxy. That's not impossible, but you can't simply take some existing type and use it like this.

If you have C 20 access, you could probably save some effort by just using a transform_view - although this may be outweighed by the work of changing everything else to use ranges rather than raw iterators.

  • Related