Подтвердить что ты не робот

Сравнение итераторов из разных контейнеров

Является ли законным сравнивать итераторы из разных контейнеров?

std::vector<int> foo;
std::vector<int> bar;

Получает ли выражение foo.begin() == bar.begin() поведение false или undefined?

(Я пишу пользовательский итератор и наткнулся на этот вопрос при реализации operator==.)

4b9b3361

Ответ 1

Если вы считаете стандарт С++ 11 (n3337):

§ 24.2.1 - [iterator.requirements.general # 6]

Итератор j называется достижимым из итератора i тогда и только тогда, когда существует конечная последовательность применений выражения ++i которой i == j. Если j достижим из i, они ссылаются на элементы той же последовательности.

§ 24.2.5 - [forward.iterators # 2]

Область == для прямых итераторов - это область итераторов в одной и той же последовательности.

Учитывая, что RandomAccessIterator должен удовлетворять всем требованиям, предъявляемым ForwardIterator, сравнение итераторов из разных контейнеров не определено.

В выпуске LWG № 446 конкретно говорится об этом вопросе, и было предложено добавить следующий текст в стандарт (спасибо @Lightness Races in Orbit за то, что он привлек к нему внимание):

Результатом прямой или косвенной оценки любой функции сравнения или двоичного оператора - с двумя значениями итератора в качестве аргументов, которые были получены из двух различных диапазонов r1 и r2 (включая их последние значения в конце), которые не являются поддиапазонами одного общего диапазона, является не определено, если явно не указано иное.

Ответ 2

Undefined поведение, насколько я знаю. В VS 2010 с

/*
* to disable iterator checking that complains that the iterators are incompatible (come from * different containers :-)
*/
#define _HAS_ITERATOR_DEBUGGING 0 

std::vector<int> vec1, vec2;

std::vector<int>::iterator it1 = vec1.begin();
std::vector<int>::iterator it2 = vec2.begin();

if (it1 == it2)
{
std::cout << "they are equal!!!"; 
}

Тест равенства возвращает в этом случае true:-), так как контейнеры пусты, а член _Ptr итераторов равен nullptr.

Кто знает, может быть, ваша реализация делает все по-другому, и тест вернет false: -).

EDIT:

См. Список библиотек активных библиотек С++ "446. Итерационное равенство между различными контейнерами". Может быть, кто-то может проверить стандарт, чтобы убедиться, что изменение было принято?

Наверное, нет, так как он включен в список активных проблем, поэтому Чарльз Бейли, который также ответил на это, прав, это неуказанное поведение.

Итак, я думаю, что поведение может отличаться (по крайней мере теоретически) между различными реализациями, и это только одна проблема.

Тот факт, что с помощью отладки итератора, включенной в реализацию STL, которая поставляется с проверками VS, используется для этого точного случая (итераторы, исходящие из разных контейнеров), по крайней мере, для меня, чтобы, по возможности, избегать подобных сравнений.

Ответ 3

Вы не можете напрямую сравнивать итераторы из разных контейнеров. Итератор - это объект, который использует внутреннее состояние контейнера для его перемещения; сравнение внутренних компонентов одного контейнера с другим просто не имеет смысла.

Однако, если доступны итераторы, являющиеся результатом container.begin(), может иметь смысл сравнивать итераторы по количеству объектов, пройденных от begin(), до текущего значения итератора. Это делается с помощью std::distance:

int a = std::distance(containerA.begin(), iteratorA);
int b = std::distance(containerB.begin(), iteratorB);

if (a <comparison> b)
{ /* ... */ }

Без дополнительного контекста трудно судить, решит ли это вашу проблему или нет. YMMV.

Ответ 4

Нет. Если это было законно, это означало бы, что указатели не будут итераторами.

Ответ 5

Я считаю, что это неопределенное поведение (С++ 03). Итераторы std::vector являются итераторами произвольного доступа, а поведение == определяется в требованиях для итераторов вперед.

== - отношение эквивалентности

Обратите внимание, что это требование для типа, поэтому оно должно быть применимо (в данном случае) к любой паре действительных (разыменованных или иначе) std::vector::iterator s. Я считаю, что это означает, что == должен дать вам ответ true/false и не может вызвать UB.

- Если a и b равны, то либо a, и b являются разыменованными, либо они не являются различимыми.

И наоборот, разыменованный итератор не может сравниться с итератором, который не является разыменованным.

- Если a и b являются разыменованными, тогда a == b тогда и только тогда, когда * a и * b являются одним и тем же объектом.

Обратите внимание на отсутствие требования о том, существует ли a == b для двух итераторов, которые не являются различимыми. Пока == транзитивен (если a.end() == b.end() и b.end() == c.end() then a.end() == c.end()), рефлексивный (a.end() == a.end()) и симметричный (если a.end() == b.end() then b.end() == a.end()), не имеет значения, если некоторые, все или нет end() итераторов в разных контейнерах сравнивают равные.

Обратите внимание, что это контрастирует с <. < определяется в терминах b - a, где a и b являются итераторами произвольного доступа. Предварительным условием выполнения b - a является то, что должно быть значение Distance n такое, что a + n == b, которое требует, чтобы a и b были итераторами в один и тот же диапазон.

Ответ 6

ISO/IEC 14882: 2003 (E) 5.10.1

Операторы == (равно) и a = = (не равные) имеют те же семантические ограничения, преобразования и тип результата, что и реляционные операторы, за исключением их более низкого приоритета и результата истины. [..] Указатели на объекты или функции того же типа (после конверсий указателей) можно сравнить для равенства. Два указателя одного типа сравнивают одинаковые, если и только если оба они равны нулю, оба указывают на одну и ту же функцию или оба представляют один и тот же адрес (3.9.2).

Результаты моделирования на XCode (3.2.3):

#include <iostream>
#include <vector>

int main()
{
    std::vector <int> a,aa;
    std::vector <float> b;

    if( a.begin() == aa.begin() )
        std::cout << "\n a.begin() == aa.begin() \n" ;

    a.push_back(10) ;

    if( a.begin() != aa.begin() )
        std::cout << "\n After push back a.begin() != aa.begin() \n" ;

    // Error if( a.begin() == b.begin() )   

    return 0;
}

Выход:

a.begin() == aa.begin()
После нажатия назад a.begin()!= Aa.begin()

Ответ 7

Я не получаю требования к итераторам ввода от стандартного 100%, но оттуда (итераторы прямого/двунаправленного/случайного доступа) нет требований к домену ==, поэтому он должен return false приводит к соотношению эквивалентности. Вы не можете делать < или > или вычитание на итераторах из разных контейнеров.

Изменить: ему не нужно возвращать значение false, оно должно приводить к соотношению эквивалентности, это позволяет .begin() двух пустых контейнеров сравнивать одинаковые (как показано в другом ответе). Если итераторы являются неразрешимыми, a == b => *a == *b должен удерживаться. Это еще не поведение undefined.