Чтение онлайн

на главную - закладки

Жанры

Эффективное использование STL
Шрифт:

ваш (правильный) код будет несправедливо отвергнут компилятором, если итераторы относятся к разным типам. Обходное решение остается прежним (перестановка

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 не существует.

Я знаю, о чем вы думаете. «Если ничего не помогает, берем кувалду», не так ли? В мире C++ это может означать лишь одно: преобразование типа. Стыдитесь. И где вы набрались таких мыслей?

Давайте разберемся с вредным заблуждением относительно преобразования типа. Посмотрим, что происходит при преобразовании

const_iterator
в
iterator
:

typedef deque<int> IntDeque; // Вспомогательные определения типов

typedef IntDeque::iterator Iter;

typedef IntDeque::const_iterator ConstIter;

ConstIter ci; // ci - const iterator

Iter i(ci); // Ошибка! He существует автоматического

// преобразования const_iterator

// в iterator

Iter i(const_cast<Iter>(ci)); // Ошибка! Преобразование const_iterator

// в iterator невозможно!

В приведенном примере используется контейнер

deque
, но аналогичный результат будет получен и для
list, set, muliset, mulimap
и хэшированных контейнеров, упоминавшихся в совете 25. Возможно, строка с преобразованием будет откомпилирована
для
vector и
string
, но это особые случаи, которые будут рассмотрены ниже.

Почему же для этих типов контейнеров преобразование не компилируется? Потому что

iterator
и
const_iterator
относятся к разным классам, и сходства между ними не больше, чем между
string
и
complex<double>
. Попытка преобразования одного типа в другой абсолютно бессмысленна, поэтому вызов
const_cast
будет отвергнут. Попытки использования
static_cast
,
reintepreter_cast
и преобразования в стиле C приведут к тому же результату.

Впрочем, некомпилируемое преобразование все же может откомпилироваться, если итераторы относятся к контейнеру

vector
или
string
. Это объясняется тем, что в реализациях данных контейнеров в качестве итераторов обычно используются указатели. В этих реализациях
vector<T>::iterator
является определением типа для
T*, vector<T>::const_iterator
— для
const T*
,
string::iterator
— для
char*
, а
string::const_iterator
— для
const char*
.
В реализациях данных контейнеров преобразование
const_iterator
в
iterator
вызовом
const_cast
компилируется и даже правильно работает, поскольку оно преобразует
const T*
в
T*
. Впрочем, даже в этих реализациях
reverse_iterator
и
const_reverse_iterator
являются полноценными классами, поэтому
const_cast
не позволяет преобразовать
const_reverse_iterator
в
reverse_iterator
. Кроме того, как объясняется в совете 50, даже реализации, в которых итераторы контейнеров
vector
и
string
представлены указателями, могут использовать это представление лишь при компиляции окончательной (release) версии. Все перечисленные факторы приводят к мысли, что преобразование
const
– итераторов в итераторы не рекомендуется и для контейнеров
vector
и
string
, поскольку переносимость такого решения будет сомнительной.

Если у вас имеется доступ к контейнеру, от которого был взят

const_iterator
, существует безопасный, переносимый способ получения соответствующего типа
iterator
без нарушения системы типов. Ниже приведена основная часть этого решения (возможно, перед компиляцией потребуется внести небольшие изменения):

typedef deque<int> IntDeque; //См. ранее

typedef IntDeque::iterator Iter;

typedef IntDeque::const_iterator ConstIter;

IntDeque d;

ConstIter ci;

… // Присвоить ci ссылку на d

Iter i(d.begin); // Инициализировать i значением d.begin

advance(i, distance(i, ci)); // Переместить i в позицию ci

Решение выглядит настолько простым и прямолинейным, что это невольно вызывает подозрения. Чтобы получить

iterator
, указывающий на тот же элемент контейнера, что и
const_iterator
, мы создаем новый
iterator
в начале контейнера и перемещаем его вперед до тех пор, пока он не удалится на то же расстояние, что и
const_iterator
! Задачу упрощают шаблоны функций
advance
и
distance
, объявленные в
<iterator>
.
Distance
возвращает расстояние между двумя итераторами в одном контейнере, a
advance
перемещает итератор на заданное расстояние. Когда итераторы
i
и
ci
относятся к одному контейнеру, выражение
advance(i, distance(i, ci))
переводит их в одну позицию контейнера.

Все хорошо, если бы этот вариант компилировался… но этого не происходит. Чтобы понять причины, рассмотрим объявление

distance
:

template<typename InputIterator>

typename iterator_traits<InputIterator>::difference_type

distance(InputIterator first, InputIterator last);

Не обращайте внимания на то, что тип возвращаемого значения состоит из 56 символов и содержит упоминания зависимых типов (таких как differenceype). Вместо этого проанализируем использование параметра-типа

InputIterator
:

template<typename InputIterator>

typename iterator_traits<InputIterator>::difference_type

distance(InputIterator first, InputIterator last);

При вызове

distance
компилятор должен определить тип, представленный
InputIterator
, для чего он анализирует аргументы, переданные при вызове. Еще раз посмотрим на вызов
distance
в приведенном выше коде:

Поделиться:
Популярные книги

Цветы сливы в золотой вазе, или Цзинь, Пин, Мэй

Ланьлинский насмешник
Старинная литература:
древневосточная литература
7.00
рейтинг книги
Цветы сливы в золотой вазе, или Цзинь, Пин, Мэй

Кодекс Охотника. Книга XIX

Винокуров Юрий
19. Кодекс Охотника
Фантастика:
фэнтези
5.00
рейтинг книги
Кодекс Охотника. Книга XIX

70 Рублей - 2. Здравствуй S-T-I-K-S

Кожевников Павел
Вселенная S-T-I-K-S
Фантастика:
боевая фантастика
постапокалипсис
5.00
рейтинг книги
70 Рублей - 2. Здравствуй S-T-I-K-S

Мастер 3

Чащин Валерий
3. Мастер
Фантастика:
героическая фантастика
попаданцы
аниме
5.00
рейтинг книги
Мастер 3

Лучше подавать холодным

Аберкромби Джо
4. Земной круг. Первый Закон
Фантастика:
фэнтези
8.45
рейтинг книги
Лучше подавать холодным

Имперец. Земли Итреи

Игнатов Михаил Павлович
11. Путь
Фантастика:
героическая фантастика
боевая фантастика
5.25
рейтинг книги
Имперец. Земли Итреи

Адвокат империи

Карелин Сергей Витальевич
1. Адвокат империи
Фантастика:
городское фэнтези
попаданцы
фэнтези
5.75
рейтинг книги
Адвокат империи

Ваше Сиятельство 2

Моури Эрли
2. Ваше Сиятельство
Фантастика:
фэнтези
альтернативная история
аниме
5.00
рейтинг книги
Ваше Сиятельство 2

Не грози Дубровскому!

Панарин Антон
1. РОС: Не грози Дубровскому!
Фантастика:
фэнтези
попаданцы
аниме
5.00
рейтинг книги
Не грози Дубровскому!

Отмороженный 7.0

Гарцевич Евгений Александрович
7. Отмороженный
Фантастика:
рпг
аниме
5.00
рейтинг книги
Отмороженный 7.0

Лолита

Набоков Владимир Владимирович
Проза:
классическая проза
современная проза
8.05
рейтинг книги
Лолита

Энфис 5

Кронос Александр
5. Эрра
Фантастика:
героическая фантастика
рпг
аниме
5.00
рейтинг книги
Энфис 5

Опасная любовь командора

Муратова Ульяна
1. Проклятые луной
Фантастика:
фэнтези
5.00
рейтинг книги
Опасная любовь командора

Найди меня Шерхан

Тоцка Тала
3. Ямпольские-Демидовы
Любовные романы:
современные любовные романы
короткие любовные романы
7.70
рейтинг книги
Найди меня Шерхан