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
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
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