Почему сравнивает с “концом ()” законный итератор?

Согласно стандарту C++ (3.7.3.2/4) использующий (не только разыменование, но также и копирование, бросок, безотносительно) недопустимый указатель является неопределенным поведением (в случае сомнения, также посмотрите этот вопрос). Теперь типичный код для пересечения STL containter похож на это:

std::vector toTraverse;
//populate the vector
for( std::vector::iterator it = toTraverse.begin(); it != toTraverse.end(); ++it ) {
    //process( *it );
}

std::vector::end() итератор на гипотетический элемент вне последнего элемента containter. Там нет никакого элемента, поэтому использование указателя через тот итератор является неопределенным поведением.

Теперь, как делает != end() работа затем? Я имею в виду, чтобы сделать сравнение, итератор должен быть создан, перенеся недопустимый адрес и затем что недопустимый адрес должен будет использоваться в сравнении, которое снова является неопределенным поведением. Действительно ли такое сравнение законно и почему?

17
задан Community 23 May 2017 в 12:08
поделиться

7 ответов

Вы правы в том, что нельзя использовать недопустимый указатель, но вы ошибаетесь, что указатель на элемент, следующий за последним элементом в array является недопустимым указателем - он действителен.

Стандарт C, раздел 6.5.6.8 говорит, что он хорошо определен и действителен:

... если выражение P указывает на последний элемент объекта массива, выражение (P) +1 указывает на один за последним элементом объекта массива ...

, но не может быть разыменовано:

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

10
ответ дан 30 November 2019 в 11:03
поделиться

Единственное требование для end () - это ++ (- end ()) == end () . end () может быть просто особым состоянием, в котором находится итератор. Нет причин, по которым итератор end () должен соответствовать любому указателю.

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

char[5] a = {'a', 'b', 'c', 'd', 'e'};
char* end = a+5;
for (char* it = a; it != a+5; ++it);

Этот код будет работать нормально и отражает ваш векторный код.

25
ответ дан 30 November 2019 в 11:03
поделиться

Помимо того, что уже было сказано (итераторы не обязательно должны быть указателями), я хотел бы указать на правило, которое вы цитируете

Согласно стандарту C ++ ( 3.7.3.2/4) использование (не только разыменование, но также копирование, преобразование и т. Д.) недопустимый указатель не определен поведение

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

int arr[5];
int *p=0;
p==arr+4; // OK
p==arr+5; // past-the-end, but OK
p==arr-1; // also OK
p==arr+123456; // not OK, according to your rule
0
ответ дан 30 November 2019 в 11:03
поделиться

Просто. Итераторы не обязательно (обязательно) указатели.

У них есть некоторые сходства (т.е. вы можете разыменовать их), но это все.

1
ответ дан 30 November 2019 в 11:03
поделиться

Реализация итератора end () контейнера стандартной библиотеки, в общем, определяется реализацией, поэтому реализация может играть трюки, которые она знает платформу для поддержки.
Если вы реализовали свои собственные итераторы, вы можете делать все, что захотите, при условии, что это соответствует стандарту. Например, ваш итератор при хранении указателя может хранить указатель NULL , чтобы указать конечный итератор. Или он может содержать логический флаг или еще много чего.

1
ответ дан 30 November 2019 в 11:03
поделиться

Да? Нет правила, которое гласит, что итераторы должны быть реализованы с использованием только указателя.

Он может иметь логический флаг, который устанавливается, когда операция приращения видит, что она проходит конец допустимых данных, например.

3
ответ дан 30 November 2019 в 11:03
поделиться

Значение, прошедшее после конца, не является недопустимым (ни для обычных массивов, ни для итераторов). Вы не можете разыменовать его, но его можно использовать для сравнений.

std::vector<X>::iterator it;

Это особый итератор. Вы можете назначить ему только действующий итератор.

std::vector<X>::iterator it = vec.end();

Это вполне допустимый итератор. Вы не можете разыменовать его, но можете использовать его для сравнения и уменьшения (при условии, что контейнер имеет достаточный размер).

5
ответ дан 30 November 2019 в 11:03
поделиться
Другие вопросы по тегам:

Похожие вопросы: