Добрый день.
Пишу свой контейнер (в образовательных целях), возникла непонятная ситуация с итераторами. Нужно реализовать iterator и const_iterator.
Оба наследуются от std::iterator (вернее оба наследуются от шаблона (в котором общий для итераторов код), который наследуется от std::iterator).
Ниже приведен упрощенный пример, он полностью рабочий (за исключением того, что вместо iterator написано norm_iterator).
Строки, помеченные (1) и (2) нужны для того чтобы можно было сравнивать iterator и const_iterator между собой.
Из-за этого пришлось добавить строчку (3), и возникла ошибка при компиляции (она возникает если заменить norm_iterator на iterator в коде):
error C2039: 'pt': is not a member of 'std::iterator<std::input_iterator_tag,T,ptrdiff_t,_Ty *,_Ty &>'
iterator_base является наследником std::iterator, а norm_iterator в свою очередь наследник iterator_base.
Ошибка в том, что операторы в строках (1) и (2), видя тип параметра (const iterator&), думают что им передают std::iterator, у которого нет члена pt, который есть у iterator.
А называть итератор иначе чем iterator рука не поднимается, хотя это решает проблему компиляции.
И отказываться от сравнения iterator и const_iterator тоже не хочется, т.к. стандартные контейнеры это умеют.
Как правильно реализовать итераторы?
Чтобы воспроизвести ошибку, в коде нужно заменить norm_iterator на iterator.
Компилятор Visual C++ 2015.
| код |
| // iterator + const_iterator
#include <iterator>
#include <iostream>
#include <conio.h>
template<class T> class C
{
T t[5];
public:
template <class X> class iterator_base : public std::iterator<std::input_iterator_tag, T>
{
X pt;
friend class C<T>;
protected:
iterator_base()
{
pt = nullptr;
}
public:
bool operator==(const iterator_base& it)
{
return pt == it.pt;
}
bool operator!=(const iterator_base& it)
{
return !operator==(it);
}
iterator_base& operator++()
{
pt++;
return *this;
}
iterator_base& operator++(int)
{
auto tmp = *this;
operator++();
return tmp;
}
const T& operator*() const
{
return *pt;
}
};
typedef typename T* PT;
typedef typename const T* CPT ;
class const_iterator; // (3) чтобы можно было написать (1) и (2)
class norm_iterator : public iterator_base<PT>
{
friend class C<T>;
public:
norm_iterator() : iterator_base<PT>()
{
}
norm_iterator(const PT& p)
{
pt = p;
}
norm_iterator(const norm_iterator& it)
{
pt = it.pt;
}
bool operator==(const norm_iterator& it)
{
return pt == it.pt;
}
bool operator!=(const norm_iterator& it)
{
return !operator==(it);
}
bool operator==(const const_iterator& it)
{
return pt == it.pt;
}
bool operator!=(const const_iterator& it)
{
return !operator==(it);
}
T& operator*()
{
return *pt;
}
};
class const_iterator : public iterator_base<CPT>
{
friend class C<T>;
public:
const_iterator() : iterator_base<CPT>()
{
}
const_iterator(const CPT& p)
{
pt = p;
}
const_iterator(const const_iterator& it)
{
pt = it.pt;
}
const_iterator(const norm_iterator& it)
{
pt = it.pt;
}
bool operator==(const const_iterator& it)
{
return pt == it.pt;
}
bool operator!=(const const_iterator& it)
{
return !operator==(it);
}
bool operator==(const norm_iterator& it) // (1)
{
return pt == it.pt;
}
bool operator!=(const norm_iterator& it) // (2)
{
return !operator==(it);
}
};
C()
{
}
norm_iterator begin()
{
return norm_iterator(&t[0]);
}
const_iterator begin() const
{
return (const_iterator(&t[0]));
}
norm_iterator end()
{
return (norm_iterator(&t[5]));
}
const_iterator end() const
{
const_iterator it;
it.pt = &t[5];
return it;
}
};
int main()
{
C<int> c;
int i = 0;
for (auto it = c.begin(); it != c.end(); it++, i++)
*it = i + 1;
const auto& cc = c;
C<int>::const_iterator it = c.begin();
for (auto x : cc)
std::cout << x << ' ';
// убедимся что можно бы сравнивать с константным итератором и наоборот
bool b00 = (c.end() == cc.end());
bool b01 = (c.end() != cc.end());
bool b10 = (cc.end() == c.end());
bool b11 = (cc.end() != c.end());
bool b20 = (cc.end() == cc.end());
bool b21 = (cc.end() != cc.end());
bool b30 = (c.end() == c.end());
bool b31 = (c.end() != c.end());
_getch();
}
|
| |
Здравствуйте, Baskak, Вы писали:
Дай итератору внятное имя.
Внутрях C<T> пиши
typedef my_new_and_shiny_iterator iterator;
как вариант костыля:
bool operator==(const typename C<T>::iterator& it) // (1)
Здравствуйте, dead0k, Вы писали:
D>Дай итератору внятное имя.
D>Внутрях C<T> пиши
typedef my_new_and_shiny_iterator iterator;
Я пробовал, вроде бы нормально, и все работает как надо, но если написать
auto x = c.begin();
и навести мышь на x, всплывет подсказка с текстом "my_new_and_shiny_iterator", хотя begin описан как
iterator begin()
Я-то понимаю что my_new_and_shiny_iterator и iterator это одно и тоже, а вот кто не знает, может не понять.
D>как вариант костыля:
D>D> bool operator==(const typename C<T>::iterator& it) // (1)
D>
А вот это помогло. А чтобы можно было писать так
bool operator==(const iterator& it) // (1)
оказалось нужно было в описании класса const_iterator добавить строчку
class const_iterator : public iterator_base<CPT>
{
friend class C<T>;
using iterator = typename C<T>::iterator; // вот это
...
но именно в классе const_iterator. Если в шаблоне C<T> вне const_iterator, как я пытался раньше, сыплется куча ошибок про отсутствие конструкторов и т.д.
В общем проблема решена, спасибо!
Здравствуйте, dead0k, Вы писали:
D>А что пишет если навести на vector<int>::begin() — ничем не смущает?
Вот блин...