При вызове передаются два параметра, i
и ci
. Параметр i
относится к типу iter
, который представляет собой определение типа для deque
. Для компилятора это означает, что InputIterator
при вызове distance
соответствует типу deque
. Однако ci
относится к типу ConstIter
, который представляет собой определение типа для deque
. Из InputIterator
соответствует типу deque
. InputIterator
никак не может соответствовать двум типам одновременно, поэтому вызов distance
завершается неудачей и каким-нибудь запутанным сообщением об ошибке, из которого можно (или нельзя) понять, что компилятор не смог определить тип InputIterator
.
Чтобы вызов нормально компилировался, необходимо ликвидировать неоднозначность. Для этого проще всего явно задать параметр-тип, используемый distance
, и избавить компилятор от необходимости определять его самостоятельно:
advanced.distance<ConstIter>(i, ci)); // Вычислить расстояние между
// i и ci (как двумя const_iterator)
// и переместить i на это расстояние
Итак, теперь вы знаете, как при помощи advance
и distance
получить iterator
, соответствующий заданному const_iterator
, но до настоящего момента совершенно не рассматривался вопрос, представляющий большой практический интерес: насколько эффективна данная методика? Ответ прост: она эффективна настолько, насколько это позволяют итераторы. Для итераторов произвольного доступа, поддерживаемых контейнерами vector
, string
, deque
и т. д., эта операция выполняется с постоянным временем. Для двусторонних итераторов (к этой категории относятся итераторы других стандартных контейнеров, а также некоторых реализаций хэшированных контейнеров — см. совет 25) эта операция выполняется с линейным временем.
Поскольку получение iterator
, эквивалентного const_iterator
, может потребовать линейного времени, и поскольку это вообще невозможно сделать при недоступности контейнера, к которому относится const_iterator
, проанализируйте архитектурные решения, вследствие которых возникла необходимость получения iterator
по const_iterator
. Результат такого анализа станет дополнительным доводом в пользу совета 26, рекомендующего отдавать предпочтение iterator
перед const
- и reverse
-итераторами.
Совет 28. Научитесь использовать функцию base
При вызове функции base
для итератора reverse_iterator
будет получен «соответствующий» iterator
, однако из сказанного совершенно не ясно, что же при этом происходит. В качестве примера рассмотрим следующий фрагмент, который заносит в вектор числа 1–5, устанавливает reverse_iterator
на элемент 3 и инициализирует iterator
функцией base
:
vector
v.reserve(5); //См. совет 14
for (int i=1; i<=5; ++i){ // Занести в вектор числа 1-5
v.push_back(i);
}
vector
find(v.rbegin, v.rend, 3);
vector
// для итератора ri
После выполнения этого фрагмента ситуация выглядит примерно так:
На рисунке видно характерное смещение reverse_iterator
и соответствующего базового итератора, воспроизводящего смещение begin
и end
по отношению к begin
и end
, но найти на нем ответы на некоторые вопросы не удается. В частности, рисунок не объясняет, как использовать i
для выполнения операций, которые должны были выполняться с ri
.
Как упоминалось в совете 26, некоторые функции контейнеров принимают в качестве параметров-итераторов только iterator
. Поэтому если вы, допустим, захотите вставить новый элемент в позицию, определяемую итератором ri
, сделать это напрямую не удастся; функция insert
контейнера vector
не принимает reverse_iterator
. Аналогичная проблема возникает при удалении элемента, определяемого итератором ri
. Функции erase не соглашаются на reverse_iterator
и принимают только iterator
. Чтобы выполнить удаление или вставку, необходимо преобразовать reverse_iterator
в iterator
при помощи base
, а затем воспользоваться iterator для выполнения нужной операции.