c++容器的迭代器失效规则是什么?

(注意:这个问答是Stack Overflow的c++ FAQ中的一个条目。关于问题本身的元讨论应该张贴在开始这一切的元问题上,而不是在这里。)


当前回答

这是cppreference.com网站上的一个很好的汇总表:

这里,插入指的是向容器中添加一个或多个元素的任何方法,擦除指的是从容器中删除一个或多个元素的任何方法。

其他回答

c++ 03(来源:迭代器失效规则(c++ 03))


插入

顺序容器

vector: all iterators and references before the point of insertion are unaffected, unless the new container size is greater than the previous capacity (in which case all iterators and references are invalidated) [23.2.4.3/1] deque: all iterators and references are invalidated, unless the inserted member is at an end (front or back) of the deque (in which case all iterators are invalidated, but references to elements are unaffected) [23.2.1.3/1] list: all iterators and references unaffected [23.2.2.3/1]

关联容器

[multi]{set,map}:所有迭代器和引用不受影响[23.1.2/8]

容器适配器

堆栈:从底层容器继承 队列:从底层容器继承 Priority_queue:从底层容器继承


擦除

顺序容器

Vector:擦除点之后的每个迭代器和引用都无效[23.2.4.3/3] Deque:所有迭代器和引用都无效,除非被擦除的成员位于Deque的末尾(前面或后面)(在这种情况下,只有被擦除的成员的迭代器和引用无效)[23.2.1.3/4] 列表:只有迭代器和被擦除元素的引用是无效的[23.2.2.3/3]

关联容器

[multi]{set,map}:只有被擦除的元素的迭代器和引用无效[23.1.2/8]

容器适配器

堆栈:从底层容器继承 队列:从底层容器继承 Priority_queue:从底层容器继承


调整

向量:根据插入/擦除[23.2.4.2/6] Deque: as per insert/erase [23.2.1.2/1] 列表:按插入/擦除[23.2.2.2/1]


注1

除非另有规定(either 显式地或通过定义函数 对于其他函数),调用 一个容器成员函数或传递 类的参数 图书馆的功能不应失效 的值的迭代器,或更改 该容器中的对象。 (23.1/11)

注2

在c++ 2003中,“end”迭代器是否服从上述规则还不清楚;无论如何,您应该假设它们是(正如实际情况一样)。

注3

指针无效的规则与引用无效的规则相同。

c++ 11(来源:迭代器失效规则(c++ 0x))


插入

顺序容器

vector: all iterators and references before the point of insertion are unaffected, unless the new container size is greater than the previous capacity (in which case all iterators and references are invalidated) [23.3.6.5/1] deque: all iterators and references are invalidated, unless the inserted member is at an end (front or back) of the deque (in which case all iterators are invalidated, but references to elements are unaffected) [23.3.3.4/1] list: all iterators and references unaffected [23.3.5.4/1] forward_list: all iterators and references unaffected (applies to insert_after) [23.3.4.5/1] array: (n/a)

关联容器

[multi]{set,map}:所有迭代器和引用不受影响[23.2.4/9]

未排序的关联容器

Unordered_ [multi]{set,map}:当重散列发生时,所有迭代器无效,但引用不受影响[23.2.5/8]。如果插入没有导致容器的大小超过z * B(其中z是最大负载因子,B是当前桶数),则不会发生重散列。(23.2.5/14)

容器适配器

堆栈:从底层容器继承 队列:从底层容器继承 Priority_queue:从底层容器继承


擦除

顺序容器

vector: every iterator and reference at or after the point of erase is invalidated [23.3.6.5/3] deque: erasing the last element invalidates only iterators and references to the erased elements and the past-the-end iterator; erasing the first element invalidates only iterators and references to the erased elements; erasing any other elements invalidates all iterators and references (including the past-the-end iterator) [23.3.3.4/4] list: only the iterators and references to the erased element is invalidated [23.3.5.4/3] forward_list: only the iterators and references to the erased element is invalidated (applies to erase_after) [23.3.4.5/1] array: (n/a)

关联容器

[multi]{set,map}:只有被擦除的元素的迭代器和引用无效[23.2.4/9]

无序关联容器

Unordered_ [multi]{set,map}:只有迭代器和被擦除元素的引用是无效的[23.2.5/13]

容器适配器

堆栈:从底层容器继承 队列:从底层容器继承 Priority_queue:从底层容器继承


调整

向量:根据插入/擦除[23.3.6.5/12] Deque: as per insert/erase [23.3.3.3/3] 列表:按插入/擦除[23.3.5.3/1] Forward_list: as per insert/erase [23.3.4.5/25] :数组(n / a)


注1

除非另有规定(either 显式地或通过定义函数 对于其他函数),调用 一个容器成员函数或传递 类的参数 图书馆的功能不应失效 的值的迭代器,或更改 该容器中的对象。 (23.2.1/11)

注2

没有swap()函数会使任何函数失效 引用、指针或迭代器 元素的元素 正在交换集装箱。[注: End()迭代器不指向任何对象 元素,因此它可能无效。 -end note] [23.2.1/10]

注3

除了上面关于swap()的警告之外,不清楚“end”迭代器是否受上述列出的每个容器规则的约束;不管怎样,你应该假设它们是。

注意4

vector and all unordered associative containers support reserve(n) which guarantees that no automatic resizing will occur at least until the size of the container grows to n. Caution should be taken with unordered associative containers because a future proposal will allow the specification of a minimum load factor, which would allow rehashing to occur on insert after enough erase operations reduce the container size below the minimum; the guarantee should be considered potentially void after an erase.

这是cppreference.com网站上的一个很好的汇总表:

这里,插入指的是向容器中添加一个或多个元素的任何方法,擦除指的是从容器中删除一个或多个元素的任何方法。

可能值得添加的是,任何类型的插入迭代器(std::back_insert_iterator, std::front_insert_iterator, std::insert_iterator)只要所有插入都通过该迭代器执行,并且没有发生其他独立的迭代器失效事件,就保证保持有效。

例如,当您使用std::insert_iterator对std::vector执行一系列插入操作时,这些插入操作很可能会触发vector重分配,这将使所有“指向”该vector的迭代器失效。但是,所讨论的插入迭代器保证是有效的,也就是说,您可以安全地继续插入序列。完全没有必要担心触发向量重新分配。

同样,这只适用于通过插入迭代器本身执行的插入。如果容器上的某个独立操作触发了迭代器失效事件,则插入迭代器也会按照一般规则失效。

例如,这段代码

std::vector<int> v(10);
std::vector<int>::iterator it = v.begin() + 5;
std::insert_iterator<std::vector<int> > it_ins(v, it);

for (unsigned n = 20; n > 0; --n)
  *it_ins++ = rand();

保证执行对向量的有效插入序列,即使向量“决定”在此过程中间重新分配。迭代器it显然会失效,但it_ins将继续有效。

由于这个问题获得了如此多的投票,并成为了一个常见问题,我想最好单独写一个答案,以提及c++ 03和c++ 11之间关于std::vector的插入操作对迭代器和引用的有效性的影响(关于reserve()和capacity()),这是投票最多的答案没有注意到的。

C++03:

重新分配将使所有引用、指针和迭代器失效 指序列中的元素。这是可以保证的 重新分配发生在调用后发生的插入期间 的大小直到插入时为止 向量的值大于最近调用中指定的大小 储备()。

C++11:

重新分配将使所有引用、指针和迭代器失效 指序列中的元素。这是可以保证的 重新分配发生在调用后发生的插入期间 的大小直到插入时为止 大于capacity()值的向量。

So in C++03, it is not "unless the new container size is greater than the previous capacity (in which case all iterators and references are invalidated)" as mentioned in the other answer, instead, it should be "greater than the size specified in the most recent call to reserve()". This is one thing that C++03 differs from C++11. In C++03, once an insert() causes the size of the vector to reach the value specified in the previous reserve() call (which could well be smaller than the current capacity() since a reserve() could result a bigger capacity() than asked for), any subsequent insert() could cause reallocation and invalidate all the iterators and references. In C++11, this won't happen and you can always trust capacity() to know with certainty that the next reallocation won't take place before the size overpasses capacity().

总之,如果您正在使用c++ 03 vector,并且希望确保在执行插入时不会发生重分配,那么应该检查大小的是先前传递给reserve()的参数的值,而不是调用capacity()的返回值,否则您可能会对“过早”重分配感到惊讶。