|
Программирование >> Включение нужных заголовков
ресуют не корни проблемы, а ее решение, которое заключается в простом изменении порядка итераторов: if (ci=i)... Обходное решение для тех случаев, когда приведенное выше сравнение не работает Подобные проблемы возникают не только при сравнении, но и вообще при смешанном использовании iterator и const i terator (или reversei terator и const reverse i terator) в одном выражении. Например, при попытке вычесть один итератор произвольного доступа из другого: if (i-ci>=3)... Если i находится минимум в трех позициях после ci... ваш (правильный) код будет несправедливо отвергнут компилятором, если итераторы относятся к разным типам. Обходное решение остается прежним (перестановка i и ci), но в этом случае приходится зитывать, что i-ci не заменяется на ci-i: if (ci+3<=i)... Обходное решение на случай, если предыдущая команда не компилируется Простейшая страховка от подобных проблем заключается в том, чтобы свести к минимуму использование разнотипных итераторов, а это в свою очередь подсказывает, что вместо consti terator следует использовать iterator. На первый взгляд отказ от const i terator только для предотвращения потенциальных недостатков реализации (к тому же имеющих обходное решение) выглядит неоправданным, но с учетом особого статуса iterator в некоторых функциях контейнеров мы неизбежно приходим к выводу, что итераторы const i terator менее практичны, а хлопоты с ними иногда просто не оправдывают затраченных усилий. Совет 27. Используйте distance и advance для преобразования constjterator в iterator Как было сказано в совете 26, некоторые функции контейнеров, вызываемые с параметрами-итераторами, ограничиваются типом iterator; const iterator им не подходит. Что же делать, если имеется const i terator и вы хотите вставить новый элемент в позицию контейнера, обозначенную этим итератором? Const i terator необходимо каким-то образом преобразовать в iterator, и вы должны принять в этом активное участие, поскольку, как было показано в совете 26, автоматического преобразования const iterator в iterator не существует. Я знаю, о чем вы думаете. Если ничего не помогает, берем кувалду , не так лй? В мире С++ это может означать лишь одно: преобразование типа. Стыдитесь. И где вы набрались таких мыслей? Давайте разберемся с вредным заблуждением относительно преобразования типа. Посмотрим, что происходит при преобразовании constjterator в iterator: typedef deque<int> IntDeque: Вспомогательные определения типов typedef IntDeque:;iterator Iter: typedef IntDeque: :constJterator Constlter: Constlter ci: ci - const iterator Iter iCci); Ошибка! Не существует автоматического преобразования constjterator в iterator Iter i(const cast<Iter>(ci)): Ошибка! Преобразование constjterator в iterator невозможно! В приведенном примере используется контейнер deque, но аналогичный результат будет ползшей и для 1 ist, set, multiset, multimap и хэшированных контейнеров, упоминавшихся в совете 25. Возможно, строка с преобразованием будет откомпилирована для vector и string, но это особые случаи, которые будут рассмотрены ниже. Почему же для этих типов контейнеров преобразование не компилируется? Потому что iterator и const i terator относятся к разным классам, и сходства между ними не больше, чем между string и complex<double>. Попытка преобразования одного типа в другой абсолютно бессмысленна, поэтому вызов const cast будет отвергнут. Попытки использования static cast, reinterpretercast и преобразования в стиле С приведут к тому же результату. Впрочем, некомпилируемое преобразование все же может откомпилироваться, если итераторы относятся к контейнеру vector или stri ng. Это объясняется тем, что в реализациях данных контейнеров в качестве итераторов обычно используются указатели. В этих реализациях vector<T>:: iterator является определением типа для Т*, vector<T>:: const iterator - для const Т*, string:: iterator - для char* a stri ng:: const i terator - для const char*. В реализациях данных контейнеров преобразование const iterator в iterator вызовом const cast компилируется и даже правильно работает, поскольку оно преобразует const Т* в Т*. Впрочем, даже в этих реализациях reversejterator и const reverse iterator являются полноценными классами, поэтому constcast не позволяет преобразовать const reverse i terator в reverse i terator. Кроме того, как объясняется в совете 50, даже реализации, в которых итераторы контейнеров vector и stri ng представлены указателями, могут использовать это представление лишь при компиляции окончательной (release) версии. Все перечисленные факторы приводят к мысли, что преобразование const-итераторов в итераторы не рекомендуется и для контейнеров vector и string, поскольку переносимость такого решения будет сомнительной. Если у вас имеется доступ к контейнеру, от которого был взят const i terator, существует безопасный, переносимый способ ползения соответствующего типа Iterator без нарушения системы типов. Ниже приведена основная часть этого решения (возможно, перед компиляцией потребуется внести небольшие изменения): typedef deque<int> IntDeque: См. ранее typedef IntDeque::iterator Iter: typedef IntDeque::constjterator Constlter: IntDeque d: Constlter ci: Присвоить ci ссылку на d Iter iCd.beginO); Инициализировать i значением d.beginO advanceCi,distanced,ci)); Переместить i в позицию ci Решение выглядит настолько простым и прямолинейным, что это невольно вызывает подозрения. Чтобы получить iterator, указывающий на тот же элемент контейнера, что и const iterator, мы создаем новый iterator в начале контейнера и перемещаем его вперед до тех пор, пока он не удалится на то же расстояние, что и const i terator! Задачу упрощают шаблоны функций advance и di stance, объявленные в <iterator>. Distance возвращает расстояние между двумя итераторами в одном контейнере, а advance перемещает итератор на заданное расстояние. Когда итераторы i и ci относятся к одному контейнеру, выражение advanceC i, di stanceC i, ci)) переводит их в одну позицию контейнера. Все хорошо, если бы этот вариант компилировался... но этого не происходит. Чтобы понять причины, рассмотрим объявление di stance: tempiate<typename InputIterator> typename iterator traits<InputIterator>:;difference type distanceCInputlterator first.Inputlterator last); He обращайте внимания на то, что тип возвращаемого значения состоит из 56 символов и содержит упоминания зависимых типов (таких как di f ferencetype). Вместо этого проанализируем использование параметра-типа Inputlterator: tempiate<typename InputIterator> typename iterator traits<InputIterator>;;difference type distancednputlterator first,Inputlterator last); При вызове distance компилятор должен определить тип, представленный Inputlterator, для чего он анализирует аргументы, переданные при вызове. Еще раз посмотрим на вызов di stance в приведенном выше коде: advanced .distanced,ci)); Переместить i в позицию ci При вызове передаются два параметра, i и ci. Параметр i относится к типу iter, который представляет собой определение типа для deque<int>:: iterator. Для компилятора это означает, что Inputlterator при вызове distance соответствует типу deque<int>: iterator. Однако ci относится к типу Constlter, который представляет собой определение типа для deque<int>: :const iterator. Из этого следует, что Inputlterator соответствует типу deque<int>: :const iterator. Inputlterator никак не может соответствовать двум типам одновременно, поэтому вызов di stance завершается неудачей и каким-нибудь запутанным сообщением об ошибке, из которого можно (или нельзя) понять, что компилятор не смог определить тип Inputlterator. Чтобы вызов нормально компилировался, необходимо ликвидировать неоднозначность. Для этого проще всего явно задать параметр-тип, используемый di stance, и избавить компилятор от необходимости определять его самостоятельно: advanced.distance<ConstIter>(i.ci)); Вычислить расстояние между i и ci (как двумя constjterator) и переместить i на это расстояние Итак, теперь вы знаете, как при помощи advance и distance получить iterator, соответствующий заданному constjterator, но до настоящего момента совершенно не рассматривался вопрос, представляющий большой практический интерес:
|
© 2006 - 2024 pmbk.ru. Генерация страницы: 0
При копировании материалов приветствуются ссылки. |