Сравнение итераторов из разных контейнеров
Является ли законным сравнивать итераторы из разных контейнеров?
std::vector<int> foo;
std::vector<int> bar;
Получает ли выражение foo.begin() == bar.begin()
поведение false или undefined?
(Я пишу пользовательский итератор и наткнулся на этот вопрос при реализации operator==
.)
Ответы
Ответ 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.