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

на главную

Жанры

Эффективное использование 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
в приведенном выше коде:

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

На границе империй. Том 6

INDIGO
6. Фортуна дама переменчивая
Фантастика:
боевая фантастика
космическая фантастика
попаданцы
5.31
рейтинг книги
На границе империй. Том 6

Курсант: Назад в СССР 4

Дамиров Рафаэль
4. Курсант
Фантастика:
попаданцы
альтернативная история
7.76
рейтинг книги
Курсант: Назад в СССР 4

С Новым Гадом

Юнина Наталья
Любовные романы:
современные любовные романы
эро литература
7.14
рейтинг книги
С Новым Гадом

Наследник

Кулаков Алексей Иванович
1. Рюрикова кровь
Фантастика:
научная фантастика
попаданцы
альтернативная история
8.69
рейтинг книги
Наследник

Приручитель женщин-монстров. Том 6

Дорничев Дмитрий
6. Покемоны? Какие покемоны?
Фантастика:
юмористическое фэнтези
аниме
5.00
рейтинг книги
Приручитель женщин-монстров. Том 6

Убивать чтобы жить 3

Бор Жорж
3. УЧЖ
Фантастика:
героическая фантастика
боевая фантастика
рпг
5.00
рейтинг книги
Убивать чтобы жить 3

Убивать, чтобы жить

Бор Жорж
1. УЧЖ
Фантастика:
героическая фантастика
боевая фантастика
рпг
5.00
рейтинг книги
Убивать, чтобы жить

Релокант. Вестник

Ascold Flow
2. Релокант в другой мир
Фантастика:
фэнтези
попаданцы
рпг
5.00
рейтинг книги
Релокант. Вестник

Неудержимый. Книга XIX

Боярский Андрей
19. Неудержимый
Фантастика:
фэнтези
попаданцы
аниме
5.00
рейтинг книги
Неудержимый. Книга XIX

Моя (не) на одну ночь. Бесконтрактная любовь

Тоцка Тала
4. Шикарные Аверины
Любовные романы:
современные любовные романы
7.70
рейтинг книги
Моя (не) на одну ночь. Бесконтрактная любовь

Папина дочка

Рам Янка
4. Самбисты
Любовные романы:
современные любовные романы
5.00
рейтинг книги
Папина дочка

Первый пользователь. Книга 3

Сластин Артем
3. Первый пользователь
Фантастика:
боевая фантастика
рпг
5.00
рейтинг книги
Первый пользователь. Книга 3

Назад в СССР: 1985 Книга 2

Гаусс Максим
2. Спасти ЧАЭС
Фантастика:
попаданцы
альтернативная история
6.00
рейтинг книги
Назад в СССР: 1985 Книга 2

В теле пацана 4

Павлов Игорь Васильевич
4. Великое плато Вита
Фантастика:
фэнтези
попаданцы
5.00
рейтинг книги
В теле пацана 4