Здравствуйте, 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.