C++ vector erase advances 迭代器

c++ vector erase advances iterator

以下简化代码有效,因为它删除了所有矢量元素。但是,我不明白为什么。由于 f.erase(r) 没有捕获将是新迭代器值的返回值,并且没有其他迭代器增量器,并且 according to documentation, erase(iterator position) 参数不是通过引用传递的,迭代器在哪里取得进步?

#include <iostream>
#include <vector>

int main ()
{
  std::vector<int> f = {1,2,3,4,5};
  auto r = f.begin();
  while (r != f.end())
  {
    std::cout << "Erasing " << *r << std::endl;
    f.erase(r);
  }
  return 0;
}
在大多数情况下,

Adding/removing 元素 to/from 向量(包括 erase())会使引用和迭代器无效。使用旧的迭代器会导致未定义的行为。

正如@Nathan 在评论中提到的那样,f.clear() 就是您所需要的。

erase 使 r 无效。之后取消引用 r 会导致未定义的行为。


但在现实世界中,除非实现有意检查迭代器有效性,否则它不会造成任何问题。

A vector 迭代器通常只存储指向元素的指针。当你删除一个元素时,它右边的所有元素都会向左移动一个位置。因此,以前被删除元素占用的内存位置将被下一个元素占用。

where does the iterator get advanced?

迭代器没有前进。您的代码似乎有效的事实纯属偶然,实际上您有未定义的行为。

cppreferencestd::vector::erase

Invalidates iterators and references at or after the point of the erase, including the end() iterator.

您不能在调用 f.erase(r); 后使用 r。如果你这样做,就会发生有趣的事情。

你必须写

  while (r != f.end())
  {
    std::cout << "Erasing " << *r << std::endl;
    r = f.erase(r);
   ^^^^^^^^^^^^^^^^
  }

因为擦除后迭代器失效

或者你可以只写

f.clear();

因为循环删除了向量的所有元素。

考虑到迭代器 r 仅在循环中使用,因此最好在使用它的循环范围内声明它。例如

  for ( auto r = f.begin(); r != f.end(); )
  {
    std::cout << "Erasing " << *r << std::endl;
    r = f.erase(r);
  }

将迭代器传递给 erase 会使该迭代器无效,因此进一步使用它(在下一次迭代中将其传递给 erase 时)具有未定义的行为。因此,该程序实际上并不 "work"。它可能看起来有效,因为这是一种可能的行为。但不能保证这种行为。

where does the iterator get advanced?

它不会,迭代器保持指向同一个位置。这在技术上是未定义的行为,但如果您考虑循环实际在做什么,您就会明白为什么会得到 "correct" 结果。

您的向量包含一个指向它存储的对象的指针。您的迭代器将指向该内存,并带有您想要的元素的偏移量。在这种情况下,它将指向数据的开头。当您擦除第一个元素时,迭代器将失效,但它仍指向向量的开头。 erase 将所有元素向前移动,因此当您进入下一次迭代时,您的状态与第一次迭代时相同,只是向量小了一个元素。您重复执行此操作,直到没有剩余元素并且 end() == begin()

你不应该依赖于这种情况总是发生,而只是使用 clear() 从向量中删除所有元素。

and according to documentation,

你应该读到最后:

vector::erase - C++ Reference

Because vectors use an array as their underlying storage, erasing elements in positions other than the vector end causes the container to relocate all the elements after the segment erased to their new positions. This is generally an inefficient operation compared to the one performed for the same operation by other kinds of sequence containers (such as list or forward_list).

Return value

An iterator pointing to the new location of the element that followed the last element erased by the function call. This is the container end if the operation erased the last element in the sequence.

vector::erase - C++ Reference

最后:

Iterator validity

Iterators, pointers and references pointing to position (or first) and beyond are invalidated, with all iterators, pointers and references to elements before position (or first) are guaranteed to keep referring to the same elements they were referring to before the call.