Is it bad practice to make an iterator that is aware of its own end

Posted by aaronman on Programmers See other posts from Programmers or by aaronman
Published on 2013-09-24T01:53:48Z Indexed on 2013/11/06 22:07 UTC
Read the original article Hit count: 243

Filed under:
|
|

For some background of why I am asking this question here is an example. In python the method chain chains an arbitrary number of ranges together and makes them into one without making copies. Here is a link in case you don't understand it. I decided I would implement chain in c++ using variadic templates. As far as I can tell the only way to make an iterator for chain that will successfully go to the next container is for each iterator to to know about the end of the container (I thought of a sort of hack in where when != is called against the end it will know to go to the next container, but the first way seemed easier and safer and more versatile).

My question is if there is anything inherently wrong with an iterator knowing about its own end, my code is in c++ but this can be language agnostic since many languages have iterators.

#ifndef CHAIN_HPP
#define CHAIN_HPP

#include "iterator_range.hpp"

namespace iter {
   template <typename ... Containers>
       struct chain_iter;
   template <typename Container>
       struct chain_iter<Container> {

        private:
           using Iterator = decltype(((Container*)nullptr)->begin());
           Iterator begin;
           const Iterator end;//never really used but kept it for consistency

        public:
           chain_iter(Container & container, bool is_end=false) :
               begin(container.begin()),end(container.end()) {
                   if(is_end) begin = container.end();
           }
           chain_iter & operator++()
           {
               ++begin;
               return *this;
           }
           auto operator*()->decltype(*begin)
           {
               return *begin;
           }
           bool operator!=(const chain_iter & rhs) const{
               return this->begin != rhs.begin;
           }
       };
   template <typename Container, typename ... Containers>
       struct chain_iter<Container,Containers...>
       {

        private:
           using Iterator = decltype(((Container*)nullptr)->begin());
           Iterator begin;
           const Iterator end;
           bool end_reached = false;
           chain_iter<Containers...> next_iter;

        public:
           chain_iter(Container & container, Containers& ... rest, bool is_end=false) :
               begin(container.begin()),
               end(container.end()),
               next_iter(rest...,is_end) {
                   if(is_end)
                       begin = container.end();
               }
           chain_iter & operator++()
           {
               if (begin == end) {
                   ++next_iter;
               }
               else {
                   ++begin;
               }
               return *this;               
           }
           auto operator*()->decltype(*begin)
           {
               if (begin == end) {
                   return *next_iter;
               }
               else {
                   return *begin;
               }
           }   
           bool operator !=(const chain_iter & rhs) const {
               if (begin == end) {
                   return this->next_iter != rhs.next_iter;
               }
               else
                   return this->begin != rhs.begin;
           }
        };
   template <typename ... Containers>
       iterator_range<chain_iter<Containers...>> chain(Containers& ... containers)
       {
           auto begin = 
               chain_iter<Containers...>(containers...);
           auto end =
               chain_iter<Containers...>(containers...,true);
           return 
               iterator_range<chain_iter<Containers...>>(begin,end);
       }
}

#endif //CHAIN_HPP

© Programmers or respective owner

Related posts about design

Related posts about c++