将元素添加到基于范围的for循环中的预分配向量是否合法?

时间:2016-02-17 21:32:51

标签: c++ visual-studio stl

我正在使用Visual Studio 2015 Update 1 C ++编译器和此代码段:

#include <iostream>
#include <vector>

using namespace std;

int main()
{
  vector<int> v{3, 1, 4};

  v.reserve(6);

  for (auto e: v)
    v.push_back(e*e);

  for (auto e: v)
    cout << e << " ";

  return 0;
}

发布版本运行正常,但调试版本生成vector iterators incompatible错误消息。那是为什么?

在将其标记为Add elements to a vector during range-based loop c++11的重复问题之前,请阅读我的回答 https://stackoverflow.com/a/35467831/219153提出相反的论点。

2 个答案:

答案 0 :(得分:17)

根据documentation

  

如果新的size()大于capacity()那么所有的迭代器和   引用(包括过去的结束迭代器)无效。   否则只有过去的迭代器无效。

它说即使容量足够,但是过去的迭代器无效,所以我相信你的代码有UB(除非这个文档不正确,标准另有说明)

答案 1 :(得分:16)

您的代码表现出未定义的行为,但它很棘手并且往往只会在调试版本中被捕获。

执行v.push_back时,如果size传递容量,则所有迭代器都将失效。你可以通过保留来避免这种情况。

但是,即使您没有使容量增长,过去的迭代器仍然会失效。通常,迭代器失效规则不区分迭代器的值&#39;将垃圾/指向不同的对象&#34;和#34;迭代器的位置&#39;不再有效&#34;。当任何一个发生时,迭代器被认为是无效的。由于结束迭代器不再是结束迭代器(它从引用到任何东西,到引用某些东西,几乎在每个实现中),标准只是声明它是无效的。

此代码:

for (auto e: v)
  v.push_back(e*e);

大致扩展:

{
  auto && __range = v; 
  for (auto __begin = v.begin(),
    __end = v.end(); 
    __begin != __end;
    ++__begin
  )
  { 
       auto e = *__begin;
       v.push_back(e*e);
  }
}

v.push_back调用使__end迭代器失效,然后将其与之进行比较,并且调试版本正确地将未定义的行为标记为问题。调试MSVC迭代器非常小心失效规则。

发布版本执行未定义的行为,并且因为向量迭代器基本上是指针周围的薄包装器,并且指向过去结束元素的指针在没有容量溢出的推回之后成为指向最后一个元素的指针,它&#34;工作&#34;。