Что произойдет, если вы увеличите итератор, равный конечному итератору контейнера STL
Что делать, если я увеличиваю итератор на 2, когда он указывает на последний элемент вектора? В этом вопросе, задающем, как настроить итератор на контейнер STL по 2 элементам, предлагаются два разных подхода:
- либо использовать форму арифметического оператора - +=2 или ++ дважды
- или использовать std:: advance ()
Я проверил оба из них с VC++ 7 для крайнего случая, когда итератор указывает на последний элемент контейнера STL или дальше:
vector<int> vec;
vec.push_back( 1 );
vec.push_back( 2 );
vector<int>::iterator it = vec.begin();
advance( it, 2 );
bool isAtEnd = it == vec.end(); // true
it++; // or advance( it, 1 ); - doesn't matter
isAtEnd = it == vec.end(); //false
it = vec.begin();
advance( it, 3 );
isAtEnd = it == vec.end(); // false
Я видел май раз советую сравнить с vector:: end () при прохождении вектора и других контейнеров:
for( vector<int>::iterator it = vec.begin(); it != vec.end(); it++ ) {
//manipulate the element through the iterator here
}
правильно ли я понимаю, что если я когда-либо использую advance() или любую операцию инкремента на итераторе и делаю это точка за концом контейнера я не смогу обнаружить эту ситуацию? Если да, то какая лучшая практика - не использовать такие достижения?
8 ответов:
Ниже приводится цитата из книги Николая Хосуттиса:
обратите внимание, что advance() не проверяет пересекает ли он конец () a последовательность (он не может проверить, потому что итераторы вообще не знают контейнеры, на которых они работают). Таким образом, вызов этой функции может результат в неопределенном поведении, потому что вызова функции operator ++ для конца последовательность не определена
иными словами, ответственность за поддержание итератор в пределах диапазона лежит полностью с вызывающим абонентом.
возможно, вы должны иметь что-то вроде этого:
template <typename Itr> Itr safe_advance(Itr i, Itr end, size_t delta) { while(i != end && delta--) i++; return i; }
вы можете перегрузить это, когда
iterator_category<Itr>
иrandom_access_iterator
сделать что-то вроде следующего:return (delta > end - i)? end : i + delta;
вы можете использовать функцию "расстояние" между вашим итератором (it) и итератором в vec.начните () и сравните его с размером вектора (полученным по размеру ()).
в этом случае ваш цикл будет выглядеть так:
for (vector<int>::iterator it = vec.begin(); distance(vec.begin(), it) < vec.size(); ++it) { // Possibly advance n times here. }
код, который предлагает Марийн, просто немного ошибочен (как указал curiousguy).
правильная версия последней строки:
bool isPastEnd = it >= vec.end();
контейнер.end () -- элемент сразу после конца -- это единственное определенное внешнее значение.
проверенный итератор будет ошибаться в том, что по существу является доступом вне диапазона, но это не очень полезно (тем более, что поведение по умолчанию заключается в завершении программы).
Я думаю, что лучше всего "не делать этого" - либо проверить каждое значение итератора (предпочтительно в чем-то обернутом как фильтр), и работать только с интересными записями, либо использовать индекс явно с
for(int i = 0; i
вы также можете сделать больше сравнений в своем заявлении for:
for( vector<int>::iterator it = vec.begin(); it != vec.end() && it+1 != vec.end(); it+=2 ) { //manipulate the element through the iterator here }
Я не знаю, как это будет работать против Костас это!--5--> предложение, но это чувствует как было бы лучше для небольшого увеличения. Конечно, это было бы довольно недостижимо для большого приращения, так как вам нужна проверка для каждого, но это еще один вариант.
Я бы определенно избегал этого, если это вообще возможно. Если вам действительно нужно увеличить на 2 значения, то рассмотрим наличие вектора std:: pair или вектора структуры с 2 элементами.
Я предлагаю вам взглянуть на импульс.Диапазон.
Это может быть безопаснее использовать.
Он также будет в C++0x.