如果递增等于STL容器的最终迭代器的迭代器会发生什么


70

如果将指向向量的最后一个元素的迭代器加2,该怎么办?在这个问题中,如何通过2个元素将迭代器调整为STL容器,提供了两种不同的方法:

  • 要么使用一种形式的算术运算符-+ = 2或++两次
  • 或使用std :: advance()

当迭代器指向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

我见过遍历vector和其他容器时可能会建议与vector :: end()进行比较:

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

显然,如果迭代器前进到循环内的最后一个元素之后,则for-loop语句中的比较将计算为false,并且循环将愉快地继续进行未定义的行为。

如果我曾经在迭代器上使用advance()或任何类型的增量操作并将其指向容器的末端,我将无法检测到这种情况吗?如果是这样,最佳实践是什么-不要使用这种进步?

Answers:


65

以下是Nicolai Josuttis书中的引文:

请注意,advance()不会检查它是否与序列的end()交叉(它无法检查,因为迭代器通常不知道它们在其上操作的容器)。因此,调用此函数可能会导致未定义的行为,因为未定义在序列末尾调用运算符++

换句话说,将迭代器保持在范围内的责任完全由调用者承担。


2
算术运算后如何检查是否在范围内?
Narek 2015年

1
@Narek你不能。在可能表现出UB的操作之后进行检查在C ++中始终是一个失败的策略。
塞巴斯蒂安·雷德尔

14

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

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;

7

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

在这种情况下,您的for循环将如下所示:

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

这是否依赖于连续的存储分配,或者是否也适用于其他容器?
sharptooth

2
它与如何为每种迭代器类型实现operator ++有关。“距离”计算必须对一个迭代器执行多少个++或-才能到达另一个迭代器。如果我是你,我会在所有我期望的容器类型上尝试一下,看看能给我多少距离。
Kostas,2009年

1
距离()适用于任何输入,前向,双向或随机存取迭代器,根据标准(24.3.4)
jalf

2
距离已针对随机访问迭代器进行了优化:其复杂度为O(1),因为它不计算++的值,但是会进行减法
康斯坦丁·丹增2009年

2
@jalf“ distance()可在任何输入迭代器上使用”:您可能不想这样做!
curiousguy 2011年

3

Marijn建议的代码略有错误(正如curiousguy指出的那样)。

最后一行的正确版本是:

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

it > vec.end()可以假定始终为假。++vec.end()是未定义的行为
Caleth

1

您还可以在for语句中进行更多比较:

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

Kostas的建议相比,我不知道这会如何执行,但感觉如果稍加调整会更好。当然,由于需要对每个选项都进行检查,因此对于较大的增量而言,这几乎是不可能维持的,但这是另一种选择。

如果可能的话,我绝对会避免的。如果确实需要一次增加2个值,请考虑使用std :: pair向量或具有2个元素的结构向量。



1

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

一个检查过的迭代器会在本质上是超出范围的访问上出错,但这并不是非常有用(特别是因为默认行为是结束程序)。

我认为最佳做法是“不要那样做”-检查迭代器的每个值(最好在包装为过滤器的东西中),并且仅对有趣的条目进行操作,或者将索引与

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

-2

即使这个问题已经使用了半年,但提及使用比较运算符>和<来检查是否迭代了容器的末尾(或迭代回头时)还是有帮助的。例如:

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

抱歉,这很对。如前所述,有效的迭代器不能超过一端的迭代器,即end()。
curiousguy

1
这仅起作用,因为std :: vector :: iterator的实现实际上是原始指针。对于另一个容器,故事可能会完全不同,当您尝试结束时,它甚至可能引发异常。
Sogartar
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.