На первый взгляд итераторы представляются предметом весьма простым. Но стоит присмотреться повнимательнее, и вы заметите, что стандартные контейнеры STL поддерживают четыре разных типа итераторов: iterator, const_iterator, reverse_iterator
и const_reverse_iterator
. Проходит совсем немного времени, и выясняется, что в некоторых формах insert и erase только один из этих четырех типов принимается контейнером. И здесь начинаются вопросы. Зачем нужны четыре типа итераторов? Существует ли между ними какая-либо связь? Можно ли преобразовать итератор от одного типа к другому? Можно ли смешивать разные типы итераторов при вызове алгоритмов и вспомогательных функций STL? Как эти типы связаны с контейнерами и их функциями?
В настоящей главе вы найдете ответы на эти вопросы, а также поближе познакомитесь с разновидностью итераторов, которой обычно не уделяют должного внимания: istreambuf_iterator
. Если вам нравится STL, но не устраивает быстродействие istream_iterator
при чтении символьных потоков, возможно, istreambuf_iterator
поможет справиться с затруднениями.
Совет 26. Старайтесь использовать iterator вместо const_iterator, reverse_iterator и const_reverse_iterator
Как известно, каждый стандартный контейнер поддерживает четыре типа итераторов. Для контейнера container
тип iterator
работает как T*
тогда как const_iterator
работает как const T*
(также встречается запись T const*
). При увеличении iterator
или const_iterator
происходит переход к следующему элементу контейнера в прямом порядке перебора (от начала к концу контейнера). Итераторы reverse_iterator
и const_reverse_iterator
также работают как T*
и const T*
соответственно, но при увеличении эти итераторы переходят к следующему элементу в обратном порядке перебора (от конца к началу).
Рассмотрим несколько сигнатур insert
и erase
в контейнере vector
:
iterator insert( iteratorposition, const T& x);
iterator erase( iteratorposition);
iterator erase( iteratorrangeBegin, iteratorrangeEnd);
Аналогичные функции имеются у всех стандартных контейнеров, но тип возвращаемого значения определяется типом контейнера. Обратите внимание: перечисленные функции требуют передачу параметров типа iterator
. Не const_iterator
, не reverse_iterator
и не const_reverse_iterator
— только iterator
. Хотя контейнеры поддерживают четыре типа итераторов, один из этих типов обладает привилегиями, отсутствующими у других типов. Тип iterator
занимает особое место.
На следующей диаграмме показаны преобразования, возможные между итераторами разных типов.
Из рисунка следует, что iterator
преобразуется в const_iterator
и reverse_iterator
, а reverse_iterator
— в const_reverse_iterator
. Кроме того, reverse_iterator
преобразуется в iterator
при помощи функции base
типа reverse_iterator
, а const_reverse_iterator
аналогичным образом преобразуется в const_iterator
. Однако из рисунка не видно, что итераторы, полученные при вызове base
, могут оказаться не теми, которые вам нужны. За подробностями обращайтесь к совету 28.
Обратите внимание: не существует пути от const_iterator
к iterator
или от const_reverse_iterator
к reverse_iterator
. Из этого важного обстоятельства следует, что const_iterator
и const_reverse_iterator
могут вызвать затруднения с некоторыми функциями контейнеров. Таким функциям необходим тип iterator
, а из-за отсутствия обратного перехода от const
-итераторов к iterator
первые становятся в целом бесполезными, если вы хотите использовать их для определения позиции вставки или удаления элементов.
Однако не стоит поспешно заключать, что const
-итераторы вообще бесполезны. Это не так. Они прекрасно работают с алгоритмами, поскольку для алгоритмов обычно подходят все типы итераторов, относящиеся к нужной категории. Кроме того, const
-итераторы подходят для многих функций контейнеров. Проблемы возникают лишь с некоторыми формами insert
и erase
.
Обратите внимание на формулировку: const
-итераторы становятся в целом бесполезными, если вы хотите использовать их для определения позиции вставки или удаления элементов. Называть их полностью бесполезными было бы неправильно. Const-итераторы могут принести пользу, если вы найдете способ получения iterator
для const_iterator
или const_reverse_iterator
. Такое возможно часто, но далеко не всегда, причем даже в благоприятном случае решение не очевидно, да и эффективным его не назовешь. В двух словах этот вопрос не изложить, если вас заинтересуют подробности — обращайтесь к совету 27. А пока имеющаяся информация позволяет понять, почему типу iterator
отдается предпочтение перед его const
- и reverse
-аналогами.
Читать дальше