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

时间:2009-06-29 11:01:23

标签: c++ stl vector iterator

当迭代器指向向量的最后一个元素时,如果将迭代器增加2,该怎么办?在this question询问如何通过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 :: end()进行比较:

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

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

我是否认为如果我在迭代器上使用advance()或任何类型的递增操作并使其指向容器的末尾,我将无法检测到这种情况?如果是这样,最佳做法是什么 - 不使用这些进步?

8 个答案:

答案 0 :(得分:59)

以下是Nicolai Josuttis的书:

  

请注意,advance()不会检查   它是否越过了一个结尾()   序列(它无法检查,因为   迭代器一般不知道   他们经营的集装箱)。   因此,调用此函数可能   导致未定义的行为,因为   在运行结束时调用operator ++   序列未定义

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

答案 1 :(得分:13)

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

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; 时执行此操作,以执行以下操作:

{{1}}

答案 2 :(得分:7)

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

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

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

答案 3 :(得分:3)

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

最后一行的正确版本是:

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

答案 4 :(得分:2)

我建议你看一下Boost.Range 使用起来可能更安全 它也将在C ++ 0x中。

答案 5 :(得分:1)

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

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

我认为最佳做法是“不要那样做” - 要么检查迭代器的每个值(最好是包含在过滤器中的东西),只对有趣的条目进行操作,要么明确地使用索引

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

答案 6 :(得分: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's建议会如何表现,但它感觉就像对于一个小的增量更好。当然,由于需要对每个增量进行检查,因此对于大量增量来说是非常难以维护的,但这是另一种选择。

如果可能的话,我肯定会避免它。如果你真的需要一次增加2个值,那么考虑使用std :: pair的向量或带有2个元素的struct的向量。

答案 7 :(得分:-1)

即使这个问题是半年之久,提及比较运算符的使用仍然有用&gt;和&lt;检查是否迭代了容器的结束(或迭代后的开始)。例如:

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