如果增加一个与STL容器的结束迭代器相等的迭代器,会发生什么

时间:2021-09-05 18:18:58

What if I increment an iterator by 2 when it points onto the last element of a vector? In this question asking how to adjust the iterator to an STL container by 2 elements two different approaches are offered:

当迭代器指向向量的最后一个元素时,如果我让它增加2,会怎么样?在这个问题中,如何通过两个元素将迭代器调整为STL容器,给出了两种不同的方法:

  • either use a form of arithmetic operator - +=2 or ++ twice
  • 使用算术运算符- +=2或+ 2的形式
  • or use std::advance()
  • 或者使用std::提前()

I've tested both of them with VC++ 7 for the edge case when the iterator points onto the last element of the STL container or beyond:

对于迭代器指向STL容器的最后一个元素或其他元素时的边缘情况,我已经用vc++ 7对它们进行了测试:

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

I've seen may times an advise to compare against vector::end() when traversing the vector and other containers:

我曾见过在遍历向量和其他容器时与vector::end()进行比较的建议:

for( vector<int>::iterator it = vec.begin(); it != vec.end(); it++ ) {
    //manipulate the element through the iterator here
}

Obviously if the iterator is advanced past the last element inside the loop the comparison in the for-loop statement will evaluate to false and the loop will happily continue into undefined behaviour.

显然,如果迭代器经过循环中的最后一个元素,那么for循环语句中的比较将被赋值为false,循环将愉快地继续进行未定义的行为。

Do I get it right that if I ever use advance() or any kind of increment operation on an iterator and make it point past the container's end I will be unable to detect this situation? If so, what is the best practice - not to use such advancements?

如果我在迭代器上使用了advance()或任何形式的增量操作,并使它指向容器的末端,我将无法检测到这种情况,我做对了吗?如果是这样,最好的做法是什么?

8 个解决方案

#1


52  

Following is the quote from Nicolai Josuttis book:

以下是Nicolai Josuttis的书:

Note that advance() does not check whether it crosses the end() of a sequence (it can't check because iterators in general do not know the containers on which they operate). Thus, calling this function might result in undefined behavior because calling operator ++ for the end of a sequence is not defined

注意,advance()不检查它是否穿过序列的end()(它不能检查,因为迭代器通常不知道它们操作的容器)。因此,调用这个函数可能会导致未定义的行为,因为没有定义在序列末尾调用操作符++

In other words, the responsibility of maintaining the iterator within the range lies totally with the caller.

换句话说,在范围内维护迭代器的责任完全在于调用者。

#2


10  

Perhaps you should have something like this:

也许你应该有这样的东西:

template <typename Itr>
Itr safe_advance(Itr i, Itr end, size_t delta)
{
    while(i != end && delta--)
        i++;
    return i;
}

You can overload this for when iterator_category<Itr> is random_access_iterator to do something like the following:

当iterator_category 是random_access_iterator时,您可以重载它,完成如下操作:

return (delta > end - i)? end : i + delta;

#3


6  

You could use the "distance" function between your iterator (it) and the iterator at vec.begin() and compare it with the vector's size (obtained by size()).

您可以使用迭代器(it)和vector .begin()的迭代器之间的“distance”函数,并将其与向量的大小(按size()获得)进行比较。

In that case your for loop would look like this:

在这种情况下,for循环看起来是这样的:

for (vector<int>::iterator it = vec.begin(); distance(vec.begin(), it) < vec.size(); ++it)
{
     // Possibly advance n times here.
}

#4


2  

The code that Marijn suggests is just slightly wrong (as curiousguy pointed out).

Marijn提出的代码只是有点错误(正如好奇的人指出的)。

The correct version of the last line is:

最后一行的正确版本是:

bool isPastEnd = it >= vec.end();

#5


1  

container.end() -- the element just past the end -- is the only defined exterior value.

container.end()——刚刚结束的元素——是惟一定义的外部值。

A checked iterator will fault on what is essentially an out-of-range access, but that isn't terribly helpful (especially as the default behaviour is to end the program).

检查过的迭代器会在本质上是超出范围的访问上出错,但这并不是很有帮助(特别是当默认行为是终止程序时)。

I think the best practice is "don't do that" -- either check every value of the iterator (preferably in something wrapped as a filter), and only operate on interesting entries, or use the index explicitly with

我认为最佳实践是“不要那样做”——要么检查迭代器的每个值(最好是作为过滤器包装的),只对有趣的条目进行操作,要么显式地使用索引

for(int i = 0; i < vec.size(); i+=2) {...}

for(int i = 0;我< vec.size();我+ = 2){…}

#6


1  

You could also do more comparisons in your for statement:

你也可以在for语句中做更多的比较:

for( vector<int>::iterator it = vec.begin(); it != vec.end() && it+1 != vec.end(); it+=2 ) {
    //manipulate the element through the iterator here
}

I don't know how this would perform vs Kostas's suggestion, but it feels like it would be better for a small increment. Of course it would be pretty unmaintainable for a large increment since you need a check for each, but it is another option.

我不知道这将如何执行与科斯塔斯的建议,但感觉它是更好的一个小增量。当然,对于一个大的增量来说,它是相当难以维护的,因为您需要对每个值进行检查,但是这是另一个选项。

I would definitely avoid it if at all possible. If you really need to increment by 2 values at a time, then consider having a vector of std::pair or a vector of a struct with 2 elements.

如果可能的话,我肯定会避免的。如果您确实需要每次增加两个值,那么考虑使用一个std:::pair或一个包含两个元素的struct的向量。

#7


1  

I suggest you to take a look at Boost.Range.
It might be safer to use.
It will also be in C++0x.

我建议你看看Boost.Range。使用它可能更安全。它也是用c++ 0x写的。

#8


-1  

Even though this question is half a year old, it might still be useful to mention the use of comparison operators > and < to check if you iterated past the end (or the start when iterating back) of the container. For example:

尽管这个问题已经有半年的历史了,但还是要提到使用比较运算符>和 <来检查您是否迭代过容器的结束(或迭代返回的开始)。例如:< p>

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

#1


52  

Following is the quote from Nicolai Josuttis book:

以下是Nicolai Josuttis的书:

Note that advance() does not check whether it crosses the end() of a sequence (it can't check because iterators in general do not know the containers on which they operate). Thus, calling this function might result in undefined behavior because calling operator ++ for the end of a sequence is not defined

注意,advance()不检查它是否穿过序列的end()(它不能检查,因为迭代器通常不知道它们操作的容器)。因此,调用这个函数可能会导致未定义的行为,因为没有定义在序列末尾调用操作符++

In other words, the responsibility of maintaining the iterator within the range lies totally with the caller.

换句话说,在范围内维护迭代器的责任完全在于调用者。

#2


10  

Perhaps you should have something like this:

也许你应该有这样的东西:

template <typename Itr>
Itr safe_advance(Itr i, Itr end, size_t delta)
{
    while(i != end && delta--)
        i++;
    return i;
}

You can overload this for when iterator_category<Itr> is random_access_iterator to do something like the following:

当iterator_category 是random_access_iterator时,您可以重载它,完成如下操作:

return (delta > end - i)? end : i + delta;

#3


6  

You could use the "distance" function between your iterator (it) and the iterator at vec.begin() and compare it with the vector's size (obtained by size()).

您可以使用迭代器(it)和vector .begin()的迭代器之间的“distance”函数,并将其与向量的大小(按size()获得)进行比较。

In that case your for loop would look like this:

在这种情况下,for循环看起来是这样的:

for (vector<int>::iterator it = vec.begin(); distance(vec.begin(), it) < vec.size(); ++it)
{
     // Possibly advance n times here.
}

#4


2  

The code that Marijn suggests is just slightly wrong (as curiousguy pointed out).

Marijn提出的代码只是有点错误(正如好奇的人指出的)。

The correct version of the last line is:

最后一行的正确版本是:

bool isPastEnd = it >= vec.end();

#5


1  

container.end() -- the element just past the end -- is the only defined exterior value.

container.end()——刚刚结束的元素——是惟一定义的外部值。

A checked iterator will fault on what is essentially an out-of-range access, but that isn't terribly helpful (especially as the default behaviour is to end the program).

检查过的迭代器会在本质上是超出范围的访问上出错,但这并不是很有帮助(特别是当默认行为是终止程序时)。

I think the best practice is "don't do that" -- either check every value of the iterator (preferably in something wrapped as a filter), and only operate on interesting entries, or use the index explicitly with

我认为最佳实践是“不要那样做”——要么检查迭代器的每个值(最好是作为过滤器包装的),只对有趣的条目进行操作,要么显式地使用索引

for(int i = 0; i < vec.size(); i+=2) {...}

for(int i = 0;我< vec.size();我+ = 2){…}

#6


1  

You could also do more comparisons in your for statement:

你也可以在for语句中做更多的比较:

for( vector<int>::iterator it = vec.begin(); it != vec.end() && it+1 != vec.end(); it+=2 ) {
    //manipulate the element through the iterator here
}

I don't know how this would perform vs Kostas's suggestion, but it feels like it would be better for a small increment. Of course it would be pretty unmaintainable for a large increment since you need a check for each, but it is another option.

我不知道这将如何执行与科斯塔斯的建议,但感觉它是更好的一个小增量。当然,对于一个大的增量来说,它是相当难以维护的,因为您需要对每个值进行检查,但是这是另一个选项。

I would definitely avoid it if at all possible. If you really need to increment by 2 values at a time, then consider having a vector of std::pair or a vector of a struct with 2 elements.

如果可能的话,我肯定会避免的。如果您确实需要每次增加两个值,那么考虑使用一个std:::pair或一个包含两个元素的struct的向量。

#7


1  

I suggest you to take a look at Boost.Range.
It might be safer to use.
It will also be in C++0x.

我建议你看看Boost.Range。使用它可能更安全。它也是用c++ 0x写的。

#8


-1  

Even though this question is half a year old, it might still be useful to mention the use of comparison operators > and < to check if you iterated past the end (or the start when iterating back) of the container. For example:

尽管这个问题已经有半年的历史了,但还是要提到使用比较运算符>和 <来检查您是否迭代过容器的结束(或迭代返回的开始)。例如:< p>

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