1

Let's consider a very basic range adaptor class which wraps around a range and iterates over every other element of the original range if you iterate over the adaptor.

for (const auto & e : everyOtherElement(originalRange))

When writing such a range adaptor class, one needs to write a corresponding iterator class for that adaptor to make it iterable and behave like desired.

Such an iterator class should implement everything required by the "concept" you want it to support, for example InputIterator. Among other things, we should implement operator* to return a reference to the represented element, as well as operator-> such that it->member accesses members of that element.

I thought that it would be a good idea to simply "forward" these operators to the implementation of the underlying iterator the adaptor wraps around (let's forget about const-ness for a moment):

struct everyOtherElement {
    OriginalIterator b, e; // The begin and end we wrap around
    // ...
    struct iterator {
        OriginalIterator it;
        auto operator*() { return *it; }                  // <------
        auto operator->() { return it.operator->(); }     // <------
        // ...
    };
};

However, the operator-> fails to compile if OriginalIterator is of pointer type, like for most std::vector implementations and of course raw arrays. Because such iterators are non-class types, it's not allowed to write it.operator->().

How should I implement the operator-> to make the iterator as transparent as possible? Should I implement operator-> in terms of operator*, i.e. like just writing (*it).m instead of it->m. I guess this fails if some iterator implements them to mean different things... (Although that would be evil, wouldn't it? Or is that forbidden by the InputIterator concept?)

Is it a good idea to implement it as just returning the original iterator, since operator-> is recursively applied automatically as long as returning a non-pointer?

        auto operator->() { return it; }
Josh Crozier
  • 202,159
  • 50
  • 343
  • 273
leemes
  • 42,229
  • 18
  • 115
  • 172
  • As I assume that there are templates involved, I would suggest just to use SFINAE/tag dispatch to select implementation depending if OriginalIterator is pointer or not. – Revolver_Ocelot Jan 22 '16 at 15:11

1 Answers1

1

See standard iterator adapters specification, for instance move_iterator:

pointer     Iterator

they define pointer type as Iterator and indeed return it by operator ->

dewaffled
  • 2,377
  • 2
  • 12
  • 27