Is it bad practice to make an iterator that is aware of its own end
- by aaronman
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