迭代器失效规则

C ++容器的迭代器失效规则是什么? 算法

最好是摘要列表格式。 数组

(注意:这原本是Stack Overflow的C ++ FAQ的条目。若是您想批评以这种形式提供FAQ的想法,那么在全部这些开始的meta上的张贴将是这样作的地方。该问题在C ++聊天室中进行监控,该问题最初是从FAQ想法开始的,因此提出这个想法的人极可能会读懂您的答案。) 安全


#1楼

可能值得补充的是,只要全部插入都是经过此迭代器执行的,而且没有其余独立的迭代器无效,则任何类型的插入迭代器( std::back_insert_iteratorstd::front_insert_iteratorstd::insert_iterator )均可以保证保持有效。事件发生。 less

例如,当您经过使用std::insert_iteratorstd::vector执行一系列插入操做时,这些插入颇有可能会触发向量从新分配,这将使“指向”该向量的全部迭代器失效。 可是,能够保证所涉及的插入迭代器保持有效,即,您能够安全地继续插入序列。 彻底没必要担忧触发向量从新分配。 函数

一样,这仅适用于经过插入迭代器自己执行的插入。 若是迭代器无效事件是由对容器的某些独立操做触发的,则插入迭代器也将根据常规规则变为无效。 spa

例如,此代码 指针

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将继续保持有效。 code


#2楼

因为这个问题吸引了不少票,而且成为了一种常见问题,因此我认为最好写一个单独的答案来说起C ++ 03和C ++ 11在std::vector的影响方面的一个显着差别。相对于reserve()和Capacity capacity()的迭代器和引用有效性的插入操做,最受支持的答案未能引发注意。 orm

C ++ 03: 对象

从新分配会使全部引用序列中元素的引用,指针和迭代器无效。 确保在调用reserve()以后发生的插入期间不会发生从新分配,直到插入使向量的大小大于最近对reserve()的调用中指定的大小为止。

C ++ 11:

从新分配会使全部引用序列中元素的引用,指针和迭代器无效。 能够保证在调用reserve()以后直到插入使向量的大小大于Capacity()的值以前,在插入期间不会发生从新分配。

所以,在C ++ 03中,正如在另外一个答案中提到的那样,它不是“ unless the new container size is greater than the previous capacity (in which case all iterators and references are invalidated) ”,而是应“ greater than the size specified in the most recent call to reserve() 。 这是C ++ 03与C ++ 11不一样的一件事。 在C ++ 03中,一旦insert()使得向量的大小达到上一个reserve()调用中指定的值(该值可能会小于当前的capacity()由于reserve()可能会致使更大的值)。 capacity()超出要求),任何后续insert()均可能致使从新分配并使全部迭代器和引用无效。 在C ++ 11中,这种状况不会发生,您能够始终信任capacity()来肯定在大小超过Capacity capacity()以前不会进行下一次从新分配。

总之,若是您使用的是C ++ 03向量,而且要确保在执行插入操做时不会发生从新分配,则应该检查大小,这是先前传递给reserve()的参数的值反对,而不是对capacity()的返回值,不然您可能会对“ 过早 ”的从新分配感到惊讶。


#3楼

C ++ 17 (全部参考均来自CPP17的最终工做草案-n4659


插入

序列容器

  • vector :功能insertemplace_backemplacepush_back缘由从新分配,若是新的尺寸比旧容量。 从新分配会使全部引用序列中元素的引用,指针和迭代器无效。 若是没有发生从新分配,则插入点以前的全部迭代器和引用均保持有效。 [26.3.11.5/1]
    关于reserve功能,从新分配会使全部引用序列中元素的引用,指针和迭代器无效。 在对reserve()的调用以后发生的插入过程当中,直到插入使向量的大小大于capacity()的值以前,都不得进行从新分配。 [26.3.11.3/6]

  • deque :在deque的中间插入会使全部迭代器和对双端队列的元素的引用无效。 在双端队列的任一端插入将使该双端队列的全部迭代器无效,但不会影响对双端队列的元素的引用的有效性。 [26.3.8.4/1]

  • list :不影响迭代器和引用的有效性。 若是引起异常,则没有效果。 [26.3.10.4/1]。
    insertemplace_frontemplace_backemplacepush_frontpush_back功能是在本规则覆盖。

  • forward_listinsert_after的重载均不影响迭代器和引用的有效性[26.3.9.5/1]

  • array一般 ,数组的迭代器在数组的整个生命周期内都不会失效。 可是,应该注意的是,在交换期间,迭代器将继续指向同一数组元素,所以将更改其值。

关联容器

  • All Associative Containersinsertemplace成员不该影响迭代器和对该容器的引用的有效性[26.2.6 / 9]

无序关联容器

  • All Unordered Associative Containers :从新哈希将使迭代器无效,更改元素之间的顺序以及更改出如今存储桶中的元素,但不会使对元素的指针或引用无效。 [26.2.7 / 9]
    insertemplace成员不该影响对容器元素的引用的有效性,但可使对容器的全部迭代器无效。 [26.2.7 / 14]
    若是(N+n) <= z * B ,则insertemplace成员不影响迭代器的有效性,其中N是插入操做以前容器中元素的数量, n是插入元素的数量, B是容器的存储桶计数, z是容器的最大装载系数。 [26.2.7 / 15]

  • All Unordered Associative Containers :在合并操做(例如a.merge(a2) )的状况下,引用已传输元素的迭代器和引用a全部迭代器都将无效,但保留在a2元素的迭代器将保持有效。 (表91 —无序关联容器要求)

容器适配器

  • stack :从基础容器继承
  • queue :从基础容器继承
  • priority_queue :从基础容器继承

清除

序列容器

  • vector :函数erasepop_back使擦除点或擦除点以后的迭代器和引用无效。 [26.3.11.5/3]

  • deque :即一个擦除的最后一个元素的擦除操做deque无效信号仅在过去的最末端迭代器和全部迭代器和引用擦除元素。 擦除deque的第一个元素而不擦除最后一个元素的擦除操做仅会使迭代器和对已擦除元素的引用无效。 不擦除deque的第一个元素或最后一个元素的擦除操做会使过去的迭代器和全部迭代器以及对deque全部元素的引用无效。 [注意: pop_frontpop_back是擦除操做。 —尾注] [26.3.8.4/4]

  • list :仅使迭代器和对已删除元素的引用无效。 [26.3.10.4/3]。 这适用于erasepop_frontpop_backclear函数。
    removeremove_if成员函数:擦除列表迭代器i引用的列表中的全部元素,并知足如下条件: *i == valuepred(*i) != false 。 仅使迭代器和对已删除元素的引用无效[26.3.10.5/15]。
    unique成员函数-擦除迭代器i引用的每一个连续连续的相等元素组中除第一个元素外的全部元素,范围为[first + 1, last) ,其中*i == *(i-1) (对于不带参数的惟一值)或pred(*i, *(i - 1)) (对于带谓词参数的不重复版本)成立。 仅使迭代器和对已擦除元素的引用无效。 [26.3.10.5/19]

  • forward_listerase_after将仅使迭代器和对已擦除元素的引用无效。 [26.3.9.5/1]。
    removeremove_if成员函数-擦除列表迭代器i所引用的列表中的全部元素,并知足如下条件: *i == value (对于remove() ), pred(*i)为true(对于remove_if() )。 仅使迭代器和对已擦除元素的引用无效。 [26.3.9.6/12]。
    unique成员函数-擦除迭代器i所引用的每一个连续连续的相等元素组中除第一个元素外的全部元素,范围为[first + 1,last],其中*i == *(i-1) (对于具备没有参数)或pred(*i, *(i - 1)) (对于带有谓词参数的版本)成立。 仅使迭代器和对已擦除元素的引用无效。 [26.3.9.6/16]

  • All Sequence Containersclear会使引用a元素的全部引用,指针和迭代器无效,而且可能使过去的迭代器无效(表87-序列容器要求)。 可是对于forward_listclear不会使过去的迭代器无效。 [26.3.9.5/32]

  • All Sequence Containersassign使全部引用容器元素的引用,指针和迭代器无效。 对于vectordeque ,还会使过去的迭代器无效。 (表87-序列容器要求)

关联容器

  • All Associative Containerserase成员应仅使迭代器和对已擦除元素的引用无效[26.2.6 / 9]

  • All Associative Containersextract成员仅使删除元素的迭代器无效; 指向被删除元素的指针和引用仍然有效[26.2.6 / 10]

容器适配器

  • stack :从基础容器继承
  • queue :从基础容器继承
  • priority_queue :从基础容器继承

与迭代器失效有关的常规容器要求:

  • 除非另有说明(显式指定或经过在其余函数中定义一个函数),不然调用容器成员函数或将容器做为参数传递给库函数均不得使对该容器内对象的迭代器或更改其值无效。 [26.2.1 / 12]

  • 没有swap()函数会使引用被交换容器的元素的任何引用,指针或迭代器无效。 [注意:end()迭代器未引用任何元素,所以可能无效。 —尾注] [26.2.1 /(11.6)]

做为上述要求的示例:

  • transform算法: opbinary_op函数不得使迭代器或子范围无效,或修改范围[28.6.4 / 1]中的元素

  • accumulate算法:在[first,last]范围内, binary_op既不得修改元素,也不得使迭代器或子范围无效[29.8.2 / 1]

  • reduce算法:binary_op既不该使迭代器或子范围无效,也不得修改[first,last]范围内的元素。 [29.8.3 / 5]

等等...


#4楼

C ++ 03 (来源: 迭代器无效规则(C ++ 03)


插入

序列容器

  • vector :插入点以前的全部迭代器和引用均不受影响,除非新的容器大小大于先前的容量(在这种状况下,全部迭代器和引用均无效)[23.2.4.3/1]
  • deque :全部迭代器和引用均无效,除非插入的成员位于双端队列的末尾(这种状况下,全部迭代器均无效,但对元素的引用不受影响)[23.2.1.3/1]
  • list :全部迭代器和引用均不受影响[23.2.2.3/1]

关联容器

  • [multi]{set,map} :全部迭代器和引用均不受影响[23.1.2 / 8]

容器适配器

  • stack :从基础容器继承
  • queue :从基础容器继承
  • priority_queue :从基础容器继承

清除

序列容器

  • vector :擦除点以后的每一个迭代器和引用都无效[23.2.4.3/3]
  • deque :全部迭代器和引用都无效,除非被删除的成员位于双端队列的末端(先后)(在这种状况下,仅迭代器和对被删除成员的引用都无效)[23.2.1.3/4]
  • list :仅迭代器和对已擦除元素的引用无效[23.2.2.3/3]

关联容器

  • [multi]{set,map} :仅迭代器和对已删除元素的引用无效[23.1.2 / 8]

容器适配器

  • stack :从基础容器继承
  • queue :从基础容器继承
  • priority_queue :从基础容器继承

调整大小

  • vector :按照插入/擦除[23.2.4.2/6]
  • deque :按照插入/擦除[23.2.1.2/1]
  • list :按插入/擦除[23.2.2.2/1]

注1

除非另有说明 (显式指定或经过在其余函数中定义一个函数),不然调用容器成员函数或将容器做为参数传递给库函数均不得使对该容器内对象的迭代器或更改其值无效 。 [23.1 / 11]

笔记2

在C ++ 2003中,尚不清楚“结束”迭代器是否受上述规则约束 ; 不管如何,您都应该假设它们是实际状况(实际状况就是如此)。

注3

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


#5楼

C ++ 11 (来源: 迭代器无效规则(C ++ 0x)


插入

序列容器

  • vector :插入点以前的全部迭代器和引用均不受影响,除非新的容器大小大于先前的容量(在这种状况下,全部迭代器和引用均无效)[23.3.6.5/1]
  • deque :全部迭代器和引用都无效,除非插入的成员位于双端队列的末尾(这种状况下,全部迭代器均无效,但对元素的引用不受影响)[23.3.3.4/1]
  • list :全部迭代器和引用均不受影响[23.3.5.4/1]
  • forward_list :全部迭代器和引用均不受影响(适用于insert_after [23.3.4.5/1]
  • array :(不适用)

关联容器

  • [multi]{set,map} :全部迭代器和引用均不受影响[23.2.4 / 9]

未分类的关联容器

  • unordered_[multi]{set,map} :从新进行哈希处理时,全部迭代器均无效,但引用不受影响[23.2.5 / 8]。 若是插入不会致使容器的大小超过z * B ,则不会进行从新哈希处理,其中z是最大装载因子, B是当前的存储桶数。 [23.2.5 / 14]

容器适配器

  • stack :从基础容器继承
  • queue :从基础容器继承
  • priority_queue :从基础容器继承

清除

序列容器

  • vector :擦除点或擦除点以后的每一个迭代器和引用都无效[23.3.6.5/3]
  • deque :删除最后一个元素只会使迭代器无效,而且对已删除元素和过去的迭代器的引用无效; 擦除第一个元素只会使迭代器和对已擦除元素的引用无效; 删除任何其余元素会使全部迭代器和引用(包括过去的迭代器)无效[23.3.3.4/4]
  • list :仅迭代器和对已擦除元素的引用无效[23.3.5.4/3]
  • forward_list :只有迭代器和引用到擦除元件被无效(适用于erase_after )[23.3.4.5/1]
  • array :(不适用)

关联容器

  • [multi]{set,map} :仅迭代器和对已删除元素的引用无效[23.2.4 / 9]

无序关联容器

  • unordered_[multi]{set,map} :仅迭代器和对已删除元素的引用无效[23.2.5 / 13]

容器适配器

  • stack :从基础容器继承
  • queue :从基础容器继承
  • priority_queue :从基础容器继承

调整大小

  • vector :按照插入/擦除[23.3.6.5/12]
  • deque :按照插入/擦除[23.3.3.3/3]
  • list :按插入/擦除[23.3.5.3/1]
  • forward_list :按照插入/擦除[23.3.4.5/25]
  • array :(不适用)

注1

除非另有说明 (显式指定或经过在其余函数中定义一个函数),不然调用容器成员函数或将容器做为参数传递给库函数均不得使对该容器内对象的迭代器或更改其值无效 。 [23.2.1 / 11]

笔记2

没有swap()函数会使引用被交换容器的元素的任何引用,指针或迭代器无效 。 [注意: end()迭代器没有引用任何元素,所以它多是无效的 。 —尾注] [23.2.1 / 10]

注3

除了上述关于swap()注意事项外, 尚不清楚“结束”迭代器是否受以上列出的每一个容器规则约束 ; 不管如何,您应该假设它们是。

注4

vector和全部无序关联容器都支持reserve(n) ,这保证至少在容器大小增加到n以前,不会自动调整大小。 应谨慎对待无序的关联容器,由于未来的建议将容许指定最小加载因子,这将容许在足够的erase操做将容器尺寸减少到最小如下以后在insert进行从新哈希处理; erase后,该担保应被视为可能无效。

相关文章
相关标签/搜索