【问题标题】:Erasing element in a vector while iterating using swap-and-pop使用交换和弹出进行迭代时擦除向量中的元素
【发布时间】:2012-12-10 15:34:08
【问题描述】:

我想在迭代向量时擦除某些元素,但下面的代码导致“向量迭代器不可递增”断言失败。

for(auto iter=vec.begin(); iter!=vec.end(); ++iter) 
{     
    if((*iter).isDead())
    {        
        std::swap(*iter, vec.back());//swap with the back       
        vec.pop_back();     //erase the element
    } 
} 

这段代码有什么问题?

【问题讨论】:

  • 寻找remove_if,如果返回isDead怎么办?
  • 我不关注。预期的结果是什么?为什么?
  • @NoSenseEtAl:在某些情况下,“交换和弹出”是对remove_if 的大规模优化,因此 IMO 值得知道如何正确地做到这一点。可以说这种情况在实践中很少见,但比较这两种情况,向量元素类型的移动/复制成本很高,并且只有一个死元素,vec[0]
  • WTF,这个Q怎么太本地化了,Steve Jessop刚刚解释了为什么知道它是件好事……我猜关闭Q的人忙着关闭Q而无法阅读所有的cmets。
  • 当然,这个问题非常重要,一点也不“过于本地化”。

标签: c++ vector


【解决方案1】:

仅当您在该迭代中不删除任何元素时才应递增迭代器:

for(auto iter=vec.begin(); iter!=vec.end();) 
{     
    if((*iter).isDead())
    {        
        std::swap(*iter, vec.back());//swap with the back       
        vec.pop_back();     //erase the element
    } 
    else
        ++iter;
}

或者更好的是,将整个循环替换为remove_if

vec.erase(std::remove_if(vec.begin(), vec.end(),
    std::bind(&ValueType::isDead, _1)), vec.end());

【讨论】:

  • 但是remove_if不会把每个元素都往下撞,这样效率低吗?
  • @kiwon: remove_if 可能效率较低,是的,因为它不必要地保留了保留元素的原始顺序。
  • @kiwon:两者都是O(n),但常数可能不同。
  • @kiwon:好的,你说得对,需要进行线性数量的移动。问题是标准在这一点上确实有些模糊。然而,很难想象一个不这样做的实现......就像 comparisons 的数量表示排序的复杂性一样,但实际上这意味着任何理智的实现都不会做多余的复制/移动。
  • @Dan:它应该最多只需要做n - k 在一个模糊合理的实现中移动。据我所知,该标准并不能保证这一点,所以如果您的库实现是由一个 numty 编写的,那么您可能会遇到这个和其他性能问题。相比之下,提问者的方法是3*k 移动(假设默认为swap),可能大于n - k,也可能小于。
【解决方案2】:

您正在递增超过与当前元素交换的元素;如果那是最后一个元素,那么您刚刚删除了它并使您的迭代器无效。如果你没有擦除,你只想增加,使用类似的东西:

for(auto iter=vec.begin(); iter!=vec.end();) 
{     
    if((*iter).isDead())
    {        
        std::swap(*iter, vec.back());//swap with the back       
        vec.pop_back();     //erase the element
    } else {
        ++iter;
    }
} 

【讨论】:

  • vec.pop_back() 可能会使iter 无效,不是吗?
猜你喜欢
  • 2012-12-03
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2013-04-06
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多