typedef IntDeque::const_iterator Constlter;
Constlter ci
// ci - const iterator
Iter i(ci);// Ошибка! He существует автоматического
// преобразования const_iterator // в iterator
Iter i(const_cast
// в iterator невозможно!
В приведенном примере используется контейнер deque, но аналогичный результат будет получен и для list, set, muliset, mulimap и хэшированных контейнеров, упоминавшихся в совете 25. Возможно, строка с преобразованием будет откомпилирована для vector и string, но это особые случаи, которые будут рассмотрены ниже.
Почему же для этих типов контейнеров преобразование не компилируется? Потому что iterator и const_iterator относятся к разным классам, и сходства между ними не больше, чем между string и complex. Попытка преобразования одного типа в другой абсолютно бессмысленна, поэтому вызов const_cast будет отвергнут. Попытки использования static_cast, reintepreter_cast и преобразования в стиле С приведут к тому же результату.
Впрочем, некомпилируемое преобразование все же может откомпилироваться, если итераторы относятся к контейнеру vector или string. Это объясняется тем, что в реализациях данных контейнеров в качестве итераторов обычно используются указатели. В этих реализациях vector является определением типа для Т*, vector — для const Т*, string::iterator — для char*, а string:: const_iterator — для const char*. В реализациях данных контейнеров преобразование const_iterator в iterator вызовом const_cast компилируется и даже правильно работает, поскольку оно преобразует const Т* в Т*. Впрочем, даже в этих реализациях reverse_iterator и const_reverse_iterator являются полноценными классами, поэтому const_cast не позволяет преобразовать const_reverse_iterator в reverse_iterator. Кроме того, как объясняется в совете 50, даже реализации, в которых итераторы контейнеров vector и string представлены указателями, могут использовать это представление лишь при компиляции окончательной (release) версии. Все перечисленные факторы приводят к мысли, что преобразование const-итераторов в итераторы не рекомендуется и для контейнеров vector и string, поскольку переносимость такого решения будет сомнительной.
Если у вас имеется доступ к контейнеру, от которого был взят const_iterator
typedef deque
typedef IntDeque::iterator Iter;
typedef IntDeque::const_iterator ConstIter;
IntDeque d;
Constlter 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, объявленные в возвращает расстояние между двумя итераторами в одном контейнере, a advance перемещает итератор на заданное расстояние. Когда итераторы i и ci относятся к одному контейнеру, выражение advance( i, distance(i, ci)) переводит их в одну позицию контейнера.
Все хорошо, если бы этот вариант компилировался... но этого не происходит. Чтобы понять причины, рассмотрим объявление distance:
template
typename iterator_traits
distance(InputIterator first, InputIterator last);
Не обращайте внимания на то, что тип возвращаемого значения состоит из 56 символов и содержит упоминания зависимых типов (таких как differenceype). Вместо этого проанализируем использование параметра-типа InputIterator:
template
typename iterator_traits