Что произойдет, если вы увеличите итератор, равный конечному итератору контейнера 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 56

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.

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

vector<int> vec;
vec.push_back( 1 );
vec.push_back( 2 );

vector<int>::iterator it = vec.begin();

it+=10; //equivalent to advance( it, 10 )
bool isPastEnd = it > vec.end(); //true