На практике выбирать обычно приходится между iterator и const_iterator
При выборе между iterator и const_iterator рекомендуется выбирать iterator даже в том случае, если можно обойтись const_iterator, а использование iterator не обусловлено необходимостью вызова функции контейнера. В частности, немало хлопот возникает при сравнениях iterator с const_iterator. Думаю, вы согласитесь, что следующий фрагмент выглядит вполне логично:
typedef deque
typedef IntDeque:iterator Iter;// упрощают работу
typedef IntDeque::const_iterator Constlter; // с контейнерами STL
// и типами итераторов
iter i;
Constlter ci:
// i и ci указывают на элементы // одного контейнера
if (i=ci)...// Сравнить iterator
В данном примере происходит обычное сравнение двух итераторов контейнера, подобные сравнения совершаются в STL сплошь и рядом. Просто один объект относится к типу iterator, а другой — к типу const_iterator. Проблем быть не должно — iterator автоматически преобразуется в const_iterator
Именно это и происходит в хорошо спроектированных реализациях STL, но в некоторых случаях приведенный фрагмент не компилируется. Причина заключается в том, что такие реализации объявляют operator
if (c=i)...// Обходное решение для тех случаев,
// когда приведенное выше сравнение не работает
Подобные проблемы возникают не только при сравнении, но и вообще при смешанном использовании iterator и const_iterator (или reverse_iterator и const_ reverse_iterator) в одном выражении. например, при попытке вычесть один итератор произвольного доступа из другого:
if (i-ci>=3)... // Если i находится минимум в трех позициях после ci...
ваш (правильный) код будет несправедливо отвергнут компилятором, если итераторы относятся к разным типам. Обходное решение остается прежним (перестановка i
и ci
), но в этом случае приходится учитывать, что i-ci
не заменяется на ci-i
:
if (c+3<=i)... // Обходное решение на случай, если
// предыдущая команда не компилируется
Простейшая страховка от подобных проблем заключается в том, чтобы свести к минимуму использование разнотипных итераторов, а это в свою очередь подсказывает, что вместо const_iterator следует использовать iterator. На первый взгляд отказ от const_iterator только для предотвращения потенциальных недостатков реализации (к тому же имеющих обходное решение) выглядит неоправданным, но с учетом особого статуса iterator в некоторых функциях контейнеров мы неизбежно приходим к выводу, что итераторы const_iterator менее практичны, а хлопоты с ними иногда просто не оправдывают затраченных усилий.
Совет 27. Используйте distance и advance для преобразования const_iterator в iterator
Как было сказано в совете 26, некоторые функции контейнеров, вызываемые с параметрами-итераторами, ограничиваются типом iterator; const_iterator им не подходит. Что же делать, если имеется const_iterator и вы хотите вставить новый элемент в позицию контейнера, обозначенную этим итератором? Const_iterator необходимо каким-то образом преобразовать в iterator, и вы должны принять в этом активное участие, поскольку, как было показано в совете 26, автоматического преобразования const_iterator в iterator не существует.
Я знаю, о чем вы думаете. «Если ничего не помогает, берем кувалду», не так ля? В мире С++ это может означать лишь одно: преобразование типа. Стыдитесь. И где вы набрались таких мыслей?
Давайте разберемся с вредным заблуждением относительно преобразования типа. Посмотрим, что происходит при преобразовании const_iterator в iterator:
typedef deque
typedef IntDeque::iterator Iter;