【问题标题】:Range-base for and insert on a vector C++11基于范围的向量和插入向量 C++11
【发布时间】:2014-04-09 16:35:39
【问题描述】:

是否可以在 C++11 中对向量进行迭代时更改其大小?明明迭代器会失效,但是下面的干净语法还能用吗?

std::vector<some_type> vec;
for(auto elem : vec) {
   if(condition(elem)) {
      new_elem = function(elem);
      vec.insert(iterator_associated_with_elem+1, new_elem);
   }
   //Don't insert on condition(new_elem)
}

如果不是,完成这项任务的最简洁的代码是什么?

【问题讨论】:

  • 有趣的是,这种类型的代码对于insert不会使迭代器无效的所有容器(例如std::liststd::setstd::map)完全有效.但是,对于缺少随机访问迭代器的类型,您必须将 +1 替换为 std::next

标签: c++ c++11 stdvector auto


【解决方案1】:

不,你不能。该标准要求raged-based for 的行为类似于给定的算法。该算法使用迭代器,当您修改向量时,它会失效。

对我来说最简单的方法是使用迭代器。请注意,当我们插入时,我们还会重新分配迭代器,以便我们始终拥有一个有效的迭代器:

auto it = vec.begin();
while(it < vec.end()) {
  if (condition(*it)) {
    new_elem = function(*it);
    it = vec.insert(it + 1, new_elem);
  }
  ++it;
}

【讨论】:

  • 实际上你可以通过一些技巧来做到这一点(见我的回答)。
  • @Danvil 当你将一个 10,000 元素的向量加倍以插入五个额外的元素时,这不是“有点小技巧”——我会说那是一路的小技巧 :-)
  • 好久没看到这个it &lt; vec.end()了……有什么理由更喜欢&lt;而不是!=吗?
  • @dyp 让迭代器的有效位置产生 true 的条件对我来说更有意义。我养成了这个习惯,这样我就可以避免更复杂的重复结构中的错误,其中迭代器可能(由于错误)跳过最后,永远不会是==。这只是一种习惯和个人喜好。
  • 感谢您的解释。我想我宁愿至少在通用代码中使用!=,因为它也适用于非 RaIt。也许你甚至可以颠倒关于这个 bug 的论点:&lt; 可以隐藏迭代中的逻辑缺陷;无论如何,迭代过去 1-past-the-end 就是 UB。
【解决方案2】:

不,你不能使用这个技巧,因为你的范围循环后面有一个迭代器。一旦该迭代器失效,您将无法再次引用它。

如果您在插入后立即退出循环,则可以使用此构造。否则,您需要使用从后面开始的基于索引的循环,并下降到零以避免“看到”在循环执行期间已插入的元素。

std::vector<some_type> vec;
for(int i = vec.size()-1 ; i >= 0 ; i--) {
   const some_type& elem(vec[i]);
   if(condition(elem)) {
      vec.insert(vec.begin()+i+1, function(elem));
   }
   //Don't insert on condition(new_elem)
}

【讨论】:

  • 实际上你可以通过一些技巧来做到这一点(见我的回答)。
【解决方案3】:
std::vector<int> vec = {1, 2, 3, 4, 5};

    for (auto &val : vec)
    {
        static int i = 0;

        if (val == 5)
        {
            vec.insert(vec.begin() + i + 1, 6);
        }
        ++i;
    }

vec 的值将是:1 2 3 4 5 6

【讨论】:

  • 我不明白为什么这是错误的 :l ideone.com/B4iuJD 这是保持 OP 基于范围的 for 循环的唯一答案,它可以工作..
  • 因为对 insert 的调用会使向量的所有迭代器无效,从而导致未定义的行为。
  • 我没有对此投反对票,但这是错误的,因为它具有未定义的行为:一旦调用 vec.insert(...),范围循环的迭代器就会失效。也许在你的代码中没问题,因为插入只发生在最后一次迭代期间,所以损害是有限的。但是,在一般情况下,您可能会读取向量在扩展期间已释放的内存。
  • @CantChooseUsernames 这是错误的,因为 6.5.4/1 显示了基于范围的伪代码实现。看看这个,原因就很明显了。
猜你喜欢
  • 2011-08-08
  • 2021-04-08
  • 1970-01-01
  • 2015-06-08
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多