У меня есть пользовательский класс контейнера , для которого я хотел бы написать iterator
и const_iterator
классы.
Я никогда не делал этого раньше, и мне не удалось найти подходящее руководство. Каковы рекомендации по созданию итераторов, и что я должен знать?
Я также хотел бы избежать дублирования кода (я чувствую это const_iterator
и iterator
делюсь многими вещами; один подкласс должен быть другим?).
Примечание: я почти уверен, что у Boost есть что-то, чтобы облегчить это, но я не могу использовать это здесь по многим глупым причинам.
c++
iterator
const-iterator
ereOn
источник
источник
Ответы:
std::iterator
с.random_access_iterator_tag
Эти базовые классы определяют все определения типов, требуемые STL, и выполняют другую работу.Во избежание дублирования кода класс итератора должен быть классом шаблона и параметризоваться как «тип значения», «тип указателя», «ссылочный тип» или все из них (зависит от реализации). Например:
Обратите внимание
iterator_type
иconst_iterator_type
определения типов: это типы для ваших неконстантных и константных итераторов.Смотрите также: ссылка на стандартную библиотеку
РЕДАКТИРОВАТЬ:
std::iterator
устарела с C ++ 17. Смотрите соответствующую дискуссию здесь .источник
random_access_iterator
не в стандарте, и ответ не обрабатывает преобразование изменяемого в const. Вы, вероятно, хотите наследовать, например,std::iterator<random_access_iterator_tag, value_type, ... optional arguments ...>
хотя.RefType operator*() { ... }
, я на шаг ближе - но это не помогает, потому что мне все еще нужноRefType operator*() const { ... }
.std::iterator
будет предложен для устаревания в C ++ 17 .std::iterator
устарелЯ собираюсь показать вам, как вы можете легко определить итераторы для ваших пользовательских контейнеров, но на всякий случай я создал библиотеку c ++ 11, которая позволяет вам легко создавать пользовательские итераторы с пользовательским поведением для любого типа контейнера, смежного или Непоследовательный.
Вы можете найти его на Github
Вот простые шаги по созданию и использованию пользовательских итераторов:
typedef blRawIterator< Type > iterator;
typedef blRawIterator< const Type > const_iterator;
iterator begin(){return iterator(&m_data[0]);};
const_iterator cbegin()const{return const_iterator(&m_data[0]);};
Наконец, на определение наших пользовательских классов итераторов:
ПРИМЕЧАНИЕ. При определении пользовательских итераторов мы производим от стандартных категорий итераторов, чтобы алгоритмы STL знали тип итератора, который мы создали.
В этом примере я определяю итератор произвольного доступа и обратный итератор произвольного доступа:
Теперь где-нибудь в вашем пользовательском классе контейнера:
источник
m_data[m_size]
is UB. Вы можете просто исправить это, заменив его наm_data+m_size
. Для обратных итераторов обаm_data[-1]
иm_data-1
неверны (UB). Для исправления reverse_iterators вам нужно будет использовать «трюки с указателями на следующий элемент».Они часто забывают, что
iterator
должны принять,const_iterator
но не наоборот. Вот способ сделать это:В приведенном выше примечании, как
IntrusiveSlistIterator<T>
преобразует вIntrusiveSlistIterator<T const>
. ЕслиT
ужеconst
это преобразование никогда не используется.источник
const
к non-const
.IntrusiveSlistIterator<T const, void>::operator IntrusiveSlistIterator<T const, void>() const
?enable_if
Может исправить это, но ...Boost может помочь: библиотека Boost.Iterator.
Точнее эта страница: boost :: iterator_adaptor .
Что очень интересно, так это учебный пример, который показывает полную реализацию с нуля для пользовательского типа.
Основной момент, как уже упоминалось, заключается в использовании единой реализации шаблона и
typedef
его.источник
// a private type avoids misuse
enabler
никогда не предназначался для того, чтобы вызывающий был провайдером, поэтому я предполагаю, что они делают его приватным, чтобы избежать случайных попыток его пропустить. Я не думаю, что это может создать какую-либо проблему для фактической передачи, поскольку защита заключается вenable_if
.Я не знаю, есть ли в Boost что-нибудь, что могло бы помочь.
Мой предпочтительный шаблон прост: возьмите аргумент шаблона, равный
value_type
, либо const квалифицирован, либо нет. При необходимости также тип узла. Тогда все становится на свои места.Просто не забудьте параметризировать (template-ize) все, что нужно, включая конструктор копирования и
operator==
. По большей части семантикаconst
создаст правильное поведение.источник
cur
из итератора противоположной константности. Решение, которое приходит на умfriend my_container::const_iterator; friend my_container::iterator;
, таково, но я не думаю, что именно так я и делал раньше ... в любом случае, этот общий план работает.friend class
в обоих случаях.Есть много хороших ответов, но я создал шаблонный заголовок, который я использую, который достаточно лаконичен и прост в использовании.
Чтобы добавить итератор в ваш класс, необходимо написать небольшой класс для представления состояния итератора с 7 небольшими функциями, из которых 2 являются необязательными:
Затем вы можете использовать его так, как вы ожидаете от итератора STL:
Я надеюсь, что это помогает.
источник