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

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

Жанры

Эффективное использование 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.98
рейтинг книги
Неучтенный. Дилогия

Бракованная невеста. Академия драконов

Милославская Анастасия
Фантастика:
фэнтези
сказочная фантастика
5.00
рейтинг книги
Бракованная невеста. Академия драконов

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

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

Жена со скидкой, или Случайный брак

Ардова Алиса
Любовные романы:
любовно-фантастические романы
8.15
рейтинг книги
Жена со скидкой, или Случайный брак

Шаман. Похищенные

Калбазов Константин Георгиевич
1. Шаман
Фантастика:
боевая фантастика
попаданцы
6.44
рейтинг книги
Шаман. Похищенные

Совок

Агарев Вадим
1. Совок
Фантастика:
фэнтези
детективная фантастика
попаданцы
8.13
рейтинг книги
Совок

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

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

Леди Малиновой пустоши

Шах Ольга
Любовные романы:
любовно-фантастические романы
6.20
рейтинг книги
Леди Малиновой пустоши

Разбуди меня

Рам Янка
7. Серьёзные мальчики в форме
Любовные романы:
современные любовные романы
остросюжетные любовные романы
5.00
рейтинг книги
Разбуди меня

Камень. Книга вторая

Минин Станислав
2. Камень
Фантастика:
фэнтези
8.52
рейтинг книги
Камень. Книга вторая

Ведьма Вильхельма

Шёпот Светлана
Любовные романы:
любовно-фантастические романы
8.67
рейтинг книги
Ведьма Вильхельма

Герцог и я

Куин Джулия
1. Бриджертоны
Любовные романы:
исторические любовные романы
8.92
рейтинг книги
Герцог и я

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

Винокуров Юрий
17. Кодекс Охотника
Фантастика:
фэнтези
попаданцы
аниме
5.00
рейтинг книги
Кодекс Охотника. Книга XVII

Плохая невеста

Шторм Елена
Любовные романы:
любовно-фантастические романы
7.71
рейтинг книги
Плохая невеста