c++iteratorreverse-iterator

reversing an iterator that depends on end()


Let's assume that I want to write a bidirectional iterator that iterates over all non-zero values of any container providing begin()/end()/rbegin()/rend(). I would have to rewrite operator++() to skip over all zeros it encounters. To make sure it is still valid, it would have to check against end() and rend() of the container each time. Something in the lines of the following:

template<class Container, class Iter> 
struct NonZeroIter: public Iter
{
  Container& c;

  using Parent = Iter;
  using Parent::Parent;
  using iterator_category = std::bidirectional_iterator_tag;

  bool is_valid() const { return *(*this) != 0; }
  bool is_end()   const { return *this == c.end(); }
  bool is_rend()  const { return *this == c.rend(); }

  NonZeroIter(Container& _c, const Iter& _it):
    Parent(_it),
    c(_c)
  { if(!is_end() && !is_valid()) ++(*this); }

  NonZeroIter& operator++()
  {
    if(!is_end()){
      do{
        Parent::operator++();
      } while(!is_end() && !is_valid());
    }
    return *this;
  }

  NonZeroIter& operator--()
  {
    if(!is_rend()){
      do{
        Parent::operator--();
      } while(!is_rend() && !is_valid());
    }
    return *this;
  }

  NonZeroIter& operator++(int) { NonZeroIter tmp(*this); ++(*this); return tmp; }
  NonZeroIter& operator--(int) { NonZeroIter tmp(*this); --(*this); return tmp; }

};

Now, I want to make a reverse-iterator of NonZeroIter using std::reverse_iterator but to do this I would have to check against rend() whenever NonZeroIter checks against end() and vice versa. Is there a nice way (avoiding overhead if possible) of doing this or do I have to write my own corresponding reverse-iterator class?


Solution

  • Based on the answer of @vll I ended up with the following code:

    // these structs compile-time translate begin,end to rbegin,rend for reverse iters
    template<class Container, class Iter>
    struct BeginEndIters
    {
      using iterator     = Iter;
      static iterator begin(Container& c) { return c.begin(); }
      static iterator end(Container& c) { return c.end(); }
    };
    template<class Container, class Iter>
    struct BeginEndIters<Container, std::reverse_iterator<Iter>>
    {
      using iterator     = std::reverse_iterator<Iter>;
      static iterator begin(Container& c) { return c.rbegin(); }
      static iterator end(Container& c) { return c.rend(); }
    };
    
    template<class Container, class Iter>
    struct NonZeroIter: public Iter
    {
      Container& c;
    
      // this is the main change
      using BeginEnd = BeginEndIters<Container, Iter>;
      // ^^^^^^^^^^^
    
      using Parent = Iter;
      using Parent::Parent;
      using iterator_category = std::bidirectional_iterator_tag;
    
      bool is_valid() const { return *(*this) != 0; }
      bool is_end()   const { return *this == BeginEnd::end(c); }
      bool is_past_begin()  const { return *this == std::prev(BeginEnd::begin(c)); }
    
      NonZeroIter(Container& _c, const Iter& _it):
        Parent(_it),
        c(_c)
      { if(!is_end() && !is_valid()) ++(*this); }
    
      NonZeroIter& operator++()
      {
        if(!is_end()){
          do{
            Parent::operator++();
          } while(!is_end() && !is_valid());
        }
        return *this;
      }
    
      NonZeroIter& operator--()
      { 
        if(!is_past_begin()){
          do{
            Parent::operator--();
          } while(!is_past_begin() && !is_valid());
        }
        return *this;
      }
    
      NonZeroIter& operator++(int) { NonZeroIter tmp(*this); ++(*this); return tmp; }
      NonZeroIter& operator--(int) { NonZeroIter tmp(*this); --(*this); return tmp; }  
    };