Re: Итератор для vector<vector<T> >
От: andrew.f  
Дата: 13.01.14 09:33
Оценка:
Здравствуйте, LightGreen, Вы писали:

LG>Подскажите, люди знающие, как грамотно сделать итератор для vector<vector<T> > так, чтобы внешняя семантика оставалась, как у итератора одного контейнера? Нужно обойти все элементы по одному разу. Типы контейнеров могут быть другими, это не принципиально, например vector<list<T> > или list<list<T> >.

LG>Я пробовал делать по-разному, но всё время возникает одна и та же проблема: непонятно, как сравнивать итераторы в конце — что считать за общий end(). Если внешний it1_ упирается в свой end(), то значение внутреннего it2_ оказывается неопределённым. Поэтому появляются трудности с реализацией функции operator==():
LG>
LG>template <class T>
LG>class MyContainer
LG>{
LG>   typedef std::vector<T> Inner;
LG>   typedef std::vector<Inner> Outer;
LG>   Outer contents_;
LG>public:
LG>   class iterator
LG>   {
LG>      friend class MyContainer;
LG>      Outer::iterator it1_;
LG>      Inner::iterator it2_, it2end_;
LG>      bool isEnd_; // <<<<< костыль
LG>      iterator(Outer::iterator it1, bool) : it1_(it1), isEnd(false) {
LG>         it2_ = it1_->begin();
LG>         it2end_ = it1_->end();
LG>      }
LG>      iterator(Outer::iterator it1) : it1_(it1), isEnd(true) {}
LG>   public:
LG>      T& operator*() { return *it2_; }
LG>      iterator& operator++() {
LG>         if(++it2_==it2end_) {
LG>            ++it1_;
LG>            it2_ = it1_->begin();
LG>            it2end_ = it1_->end();
LG>         }
LG>      }
LG>      bool operator==(iterator it) const {
LG>         return it1_==it.it1_ &&
LG>            (isEnd_==it.isEnd_ || it2_==it.it2_);
LG>      }
LG>   };
   
LG>   iterator begin() { return iterator(contents_.begin(), true); }
LG>   iterator end() { return iterator(contents_.end()); }
LG>};
LG>


LG>В этом коде используется костыль в виде дополнительной переменной isEnd_, которая устанавливается в true для конца первого контейнера (когда значения it2_ и it2end_ не определены). Это увеличивает размер данных под итератор и количество операций сравнения в шаге цикла for(;it!=itEnd; ). Можно ли данную задачу решить изящнее, без дополнительной переменной и дополнительного сравнения?


А вообще зачем в операторе сравнения сравнивать два итератора и it1_ и it2_?
По идее достаточно сравнить только it2_ == it.it2_? Не?

Судя по коду в operator++() у тебя отсутствует промежуточная ситуация, когда it1_ валидный, а it2_ == it2end_. Всё потому, что it2_ сразу перепрыгивает на начало следующего диапазона, когда достигается it2end_. То есть it2_ равен неоднозначному it2end_ только в одном состоянии — когда достигается конец контейнера. Неоднозначный он потому, что может содержать один и тот же указатель (в общем зависит от реализации STL, например NULL).
Поскольку, итератор — это указатель, то валидный итератор из одного диапазона не может оказаться равным валидного итератору из другого диапазона.
Получается что твой сборный итератор окажется равным end() тогда, когда it2_ == Inner->back().end() — это и есть end() твоего контейнера.

Остается поиграться c инициализацией этого итератора. Тут и возникает проблема с инициализацией end(), когда контейнер пустой.
НО, проблема ли это? Ты при пустой инициализации, можешь в Inner контейнер сувать пустой Outer. Таким образом наш Inner всегда содержит данные, даже в пустом состоянии, то есть его begin() всегда валидный, а значит всегда есть Inner->back().end(), который и является end()-итератором сборного контейнера.

Хотя, конечно, STL не оговаривает ситуацию сравнения итераторов из разных контейнеров. Но это совсем какой то экзотический STL.
 
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.