Я сделал коллекцию, для которой хочу предоставить итератор с произвольным доступом в стиле STL. Я искал пример реализации итератора, но не нашел. Я знаю о необходимости постоянных перегрузок []
и *
операторов. Какие требования предъявляются к итератору в стиле «STL» и каких других ошибок следует избегать (если таковые имеются)?
Дополнительный контекст: это для библиотеки, и я не хочу вводить какую-либо зависимость от нее, если мне действительно не нужно. Я пишу свою собственную коллекцию, чтобы обеспечить двоичную совместимость между C ++ 03 и C ++ 11 с одним и тем же компилятором (поэтому нет STL, который, вероятно, сломался бы).
c++
iterator
const-iterator
Тамас Селеи
источник
источник
Ответы:
http://www.cplusplus.com/reference/std/iterator/ имеет удобную диаграмму, которая детализирует спецификации § 24.2.2 стандарта C ++ 11. По сути, итераторы имеют теги, которые описывают допустимые операции, а теги имеют иерархию. Ниже приведен чисто символический, эти классы на самом деле не существуют как таковые.
Вы можете либо специализироваться
std::iterator_traits<youriterator>
, либо помещать те же typedefs в сам итератор, либо наследовать отstd::iterator
(у которых есть эти typedefs). Я предпочитаю второй вариант, чтобы избежать изменений вstd
пространстве имен, и для удобства чтения, но большинство людей наследуют отstd::iterator
.Обратите внимание , что iterator_category должен быть один из
std::input_iterator_tag
,std::output_iterator_tag
,std::forward_iterator_tag
,std::bidirectional_iterator_tag
, илиstd::random_access_iterator_tag
, в зависимости от того, какие требования вашего итератор удовлетворяет. В зависимости от вашего итератора, вы можете выбрать специализациюstd::next
,std::prev
,std::advance
иstd::distance
как хорошо, но это редко. В крайне редких случаях вы можете захотеть специализироватьсяstd::begin
иstd::end
.Ваш контейнер, вероятно, также должен иметь
const_iterator
итератор (возможно, изменяемый) для константных данных, аналогичный вашему,iterator
за исключением того, что он должен быть неявно создан из a,iterator
и пользователи не смогут изменять данные. Обычно его внутренний указатель является указателем на непостоянные данные иiterator
наследуется от него,const_iterator
чтобы минимизировать дублирование кода.Мой пост в « Написание собственного STL-контейнера» содержит более полный прототип контейнера / итератора.
источник
std::iterator_traits
тому, что вы сами специализируете или определяете typedef, вы также можете просто извлечь из негоstd::iterator
, который определяет их для вас, в зависимости от его параметров шаблона.const_iterator
. Чего еще не хватало в моем сообщении? Похоже, вы подразумеваете, что есть еще что добавить в класс, но вопрос конкретно о реализации итераторов.std::iterator
было предложено объявить устаревшим в C ++ 17 ; это не так, но я бы не стал рассчитывать на то, что это случится гораздо дольше.std::iterator
конце концов, устарело.operator bool
невероятно опасно. Кто-то попытается использовать это для определения конца диапазонаwhile(it++)
, но все, что он действительно проверяет, - это если итератор был создан с параметром.Документация iterator_facade от Boost.Iterator предоставляет то, что выглядит как хорошее руководство по реализации итераторов для связанного списка. Не могли бы вы использовать это в качестве отправной точки для создания итератора произвольного доступа над вашим контейнером?
Если ничего другого, вы можете взглянуть на функции-члены и typedefs, предоставляемые
iterator_facade
и использовать его в качестве отправной точки для создания своих собственных.источник
Томас Беккер написал полезную статью на эту тему здесь .
Был также такой (возможно, более простой) подход, который появился ранее в SO: как правильно реализовать пользовательские итераторы и const_iterators?
источник
Вот пример необработанного итератора указателя.
Вы не должны использовать класс итератора для работы с необработанными указателями!
Обходной путь цикла на основе необработанного указателя. Пожалуйста, поправьте меня, если есть лучший способ сделать цикл на основе диапазона из необработанного указателя.
И простой тест
источник
Прежде всего вы можете посмотреть здесь список различных операций, которые должны поддерживать отдельные типы итераторов.
Затем, когда вы создали свой класс итератора, вам нужно либо специализироваться
std::iterator_traits
для него и предоставить некоторые необходимыеtypedef
s (например,iterator_category
илиvalue_type
), либо альтернативно получить егоstd::iterator
, который определяет необходимыеtypedef
для вас s и, следовательно, может использоваться по умолчаниюstd::iterator_traits
.Отказ от ответственности: я знаю, что некоторым людям это не очень нравится
cplusplus.com
, но они предоставляют действительно полезную информацию по этому вопросу.источник
Я был / я в той же лодке, что и вы, по разным причинам (частично образовательным, частично ограниченным). Мне пришлось переписать все контейнеры стандартной библиотеки, и контейнеры должны были соответствовать стандарту. Это означает, что если я поменяю контейнер с версией stl , код будет работать так же. Что также означало, что я должен был переписать итераторы.
Во всяком случае, я посмотрел на EASTL . Помимо изучения тонны контейнеров, которую я никогда не изучал все это время, используя контейнеры stl или мои курсы для студентов. Основная причина в том, что EASTL более читабелен , чем аналог stl (я обнаружил, что это просто из-за отсутствия всех макросов и простого стиля кодирования). Там есть какие-то странные вещи (например, #ifdefs для исключений), но ничего не ошеломляет.
Как уже упоминалось, посмотрите ссылку cplusplus.com на итераторы и контейнеры.
источник
Я пытался решить проблему возможности перебирать несколько различных текстовых массивов, каждый из которых хранится в большой резидентной базе данных
struct
.Следующее было разработано с использованием Visual Studio 2017 Community Edition в тестовом приложении MFC. Я привожу это в качестве примера, так как эта публикация была одной из нескольких, с которыми я столкнулся, предоставляя некоторую помощь, но все еще не отвечающую моим потребностям.
struct
содержащих резидентных данных Памяти выглядели примерно следующий. Для краткости я удалил большинство элементов и также не включил используемые определения препроцессора (используемый SDK предназначен для C, а также C ++ и является старым).Мне было интересно иметь итераторы для различных
WCHAR
двумерных массивов, которые содержали текстовые строки для мнемоники.Текущий подход состоит в том, чтобы использовать шаблон для определения прокси-класса для каждого из массивов, а затем иметь один класс итераторов, который можно использовать для итерации по определенному массиву, используя прокси-объект, представляющий массив.
Копия резидентных данных памяти сохраняется в объекте, который обрабатывает чтение и запись резидентных данных памяти с / на диск. Этот класс
CFilePara
содержит шаблонный прокси-класс (MnemonicIteratorDimSize
и подкласс, из которого он полученMnemonicIteratorDimSizeBase
) и класс итератораMnemonicIterator
.Созданный прокси-объект присоединяется к объекту итератора, который получает доступ к необходимой информации через интерфейс, описываемый базовым классом, из которого получены все прокси-классы. В результате получается один тип класса итератора, который можно использовать с несколькими разными прокси-классами, поскольку все разные прокси-классы предоставляют один и тот же интерфейс - интерфейс базового прокси-класса.
Первым делом нужно было создать набор идентификаторов, которые будут предоставлены фабрике классов для генерации конкретного прокси-объекта для этого типа мнемоники. Эти идентификаторы используются как часть пользовательского интерфейса для идентификации конкретных данных обеспечения, которые пользователь заинтересован в просмотре и, возможно, изменении.
Прокси-класс
Шаблонный прокси-класс и его базовый класс следующие. Мне нужно было разместить несколько различных типов
wchar_t
текстовых строковых массивов. Двухмерные массивы имели различное количество мнемоник, в зависимости от типа (цели) мнемоники, и разные типы мнемоник имели различную максимальную длину, варьируясь от пяти текстовых символов до двадцати текстовых символов. Шаблоны для производного прокси-класса естественно соответствовали шаблону, требующему максимального количества символов в каждой мнемонике. После создания прокси-объекта мы используемSetRange()
метод для указания фактического мнемонического массива и его диапазона.Класс Iterator
Сам класс итератора выглядит следующим образом. Этот класс предоставляет только основные функциональные возможности прямого итератора, и это все, что нужно в данный момент. Однако я ожидаю, что это изменится или будет расширено, когда мне понадобится что-то дополнительное.
Фабрика прокси-объектов определяет, какой объект должен быть создан на основе мнемонического идентификатора. Прокси-объект создается, и возвращаемый указатель является стандартным типом базового класса, чтобы иметь единый интерфейс независимо от того, к какому из различных мнемонических разделов обращаются. Этот
SetRange()
метод используется для указания прокси-объекту конкретных элементов массива, которые представляет прокси-сервер, и диапазона элементов массива.Использование прокси-класса и итератора
Прокси-класс и его итератор используются, как показано в следующем цикле, для заполнения
CListCtrl
объекта списком мнемоник. Я используюstd::unique_ptr
так, что когда прокси-класс мне больше не нужен иstd::unique_ptr
выходит из области видимости, память будет очищена.Этот исходный код создает прокси-объект для массива,
struct
который соответствует указанному мнемоническому идентификатору. Затем он создает итератор для этого объекта, использует ранжированиеfor
для заполнения элементаCListCtrl
управления, а затем очищает. Это все необработанныеwchar_t
текстовые строки, которые могут точно соответствовать количеству элементов массива, поэтому мы копируем строку во временный буфер, чтобы гарантировать, что текст заканчивается нулем.источник
А теперь ключевой итератор для цикла на основе диапазона.
Использование:
Это то, что я искал. Но, похоже, никто этого не имел.
Вы получаете мое выравнивание кода OCD в качестве бонуса.
В качестве упражнения напишите свое
values(my_map)
источник