# How to compare two non-random access iterator in C++

 Refresh December 2018 Views 165 time
1

I have this :

``````vector<int> vec = {10, 4, 18, 7, 2, 10, 25, 30};
auto& pos25 = find(vec.cbegin(), vec.cend(), 25);
auto& pos18 = find(vec.cbegin(), vec.cend(), 18);
``````

Now, i want to make a query to search for 7 between there two positions. I can just use `operator<` between `pos25` and `pos18` since they're random acess iterators and then I can `find` the location of 7 within that range.

But what if my container is a `forward_list`. How will I implement that since I don't have an `operator<` to compare these two iterators; hence I can't know whether `pos25` or `pos18` occur first to give a range to the `find` function.

I found this method in a book :

``````pos18 = find (vec.begin(), vec.end(), // range
18); // value
pos25 = find (vec.begin(), pos18, // range
25); // value
if (pos18 != coll.end() && pos25 != pos18) {
// pos25 is in front of pos18
// so, only [pos25,pos18) is valid
...
}
else {
pos25 = find (pos25, vec.end(), // range
25); // value
if (pos25 != coll.end()) {
// pos18 is in front of pos25
// so, only [pos18,pos25) is valid
...
}
else {
// 18 and/or 25 not found
...
}
}
``````

Though this is simple enough, is there anything more efficient?

### 3 answers

3

Для начала этот фрагмент кода (если обновить опечатку) неправильно

``````vector<int> vec = {10, 4, 18, 7, 2, 10, 25, 30};
auto& pos25 = find(vec.cbegin(), vec.cend(), 25);
auto& pos18 = find(vec.cbegin(), vec.cend(), 18);
``````

Вы не можете связать временный объект с непостоянной ссылкой.

Что касается этого подхода

``````pos18 = find (vec.begin(), vec.end(), // range
18); // value
pos25 = find (vec.begin(), pos18, // range
25); // value
``````

то вам нужно будет проверить много условий. Например перед вызовом

``````pos25 = find (vec.begin(), pos18, // range
25); // value
``````

вы должны проверить , `pos19`не равно `vec.end()`.

Что касается определения того, какого итератор меньше или больше , чем вы можете использовать стандартную функцию `std::distance`. Однако это неэффективно применительно к итераторам контейнера `std::forward_list`.

Более эффективный подход заключается в использовании стандартного алгоритма `std::find_first_of`вместо алгоритма `std::find`для поиска первого итератора диапазона.

Вот показательная программа

``````#include <iostream>
#include <vector>
#include <algorithm>
#include <iterator>

int main()
{
std::vector<int> vec = { 10, 4, 18, 7, 2, 10, 25, 30 };
int a[] = { 25, 18 };

auto first = std::find_first_of(vec.cbegin(), vec.cend(),
std::begin(a), std::end(a));

auto last = vec.cend();
auto target = vec.cend();

if (first != vec.cend())
{
last = std::find(first, vec.cend(),
*first == a ? a : a);
}

if (last != vec.cend())
{
target = std::find(first, last, 7);
}

if (target != vec.end())
{
std::cout << 7 << " is between " << *first
<< " and " << *last << std::endl;
}
}
``````

Выход программы

``````7 is between 18 and 25
``````
3

Перебор связанного списка является относительно высокой стоимости из-за потенциал обращения к памяти, которые должны быть сделаны. Вы хотите, чтобы свести к минимуму этих доступов. Есть несколько вещей, которые вы могли бы сделать с этой целью:

1. Используйте `find_if`для поиска либо 18 или 25
2. Тогда поиск с этой точкой с `find_if`снова либо 7 или другой оценкой
3. Если другой связан был найдено 1 - й не вмешиваясь 7 , если 7 было найдено первым обеспечить другую грань существует

Так что ваш код может выглядеть следующим образом:

``````const auto start = find_if(cbegin(vec), cend(vec), [](const auto& i){ return i == 18 || i == 25; });
const auto target = find_if(start, cend(vec), [finish = *start == 18 ? 25 : 18](const auto& i){ return i == 7 || i == finish; });
const auto finish = *target == 7 ? find(target, cend(vec), *start == 18 ? 25 : 18) : cend(vec);
``````

После этого , если `finish`не указывает на `cend(vec)`то `target`действительный указатель на 1 - й 7 в диапазоне.

Live Example

Влад из решения Москвы хитро избежать необходимости лямбды с помощи `find_first_of`, но итерация над содержимым `vec`более чем один раз, что делает его более дорогим , чем мой алгоритм. Брак этих 2 -х алгоритмов приводит к алгоритму , который быстрее , чем мой оригинал, сохраняя преимущество только доступ к каждому элементу один раз:

``````const int a[] = { 18, 25 };
const auto start = find_first_of(cbegin(vec), cend(vec), cbegin(a), cend(a));
const int b[] = { *start == *cbegin(a) ? *crbegin(a) : *cbegin(a), 7 };
const auto target = find_first_of(start, cend(vec), cbegin(b), cend(b));
const auto finish = *target == *crbegin(b) ? find(target, cend(vec), *cbegin(b)) : cend(vec);
``````

Опять же, если `finish`не указывает на `cend(vec)`то `target`действительный указатель на 1 - й 7 в диапазоне.

Live Example

0

У вас нет никаких вариантов для сравнения два `ForwardIterator`отличных «S `operator ==`. Это означает , что у вас есть только два пути здесь:

1. Используйте `std::find`для обеспечения итераторы принадлежат к определенной части вашего массива. Этот метод реализован в коде вы цитируемой и в @ ответ Джонатана.
2. Сравните два итератора, написав специальную процедуру для таких операций.

Например, вы можете написать следующий код:

``````template<typename ForwardIterator>
bool less(ForwardIterator lhs, ForwardIterator rhs, ForwardIterator end) {
if (lhs == rhs) {
return false; // Equal
}
while (lhs++ != end) {
if (lhs == rhs) {
return true; // rhs is after lhs
}
}
return false; // lhs is after rhs
}
``````

Обратите внимание, что эта процедура предполагает, что оба итератора принадлежит к одной и той же емкости и имеет линейную временную сложность.

Лично я рекомендовал бы в такой ситуации , используя `RandomAccessIterator`. Да, его `std::list`не существует, но вы можете использовать `std::vector`вместо этого.