C++有关unordered_map::erase的奇怪bug

背景

项目中使用unordered_map保存方便使用key快速索引对象
需要每次使用后根据key判断是否删除过期pair代码如下:

for(auto& pair : m)
{   
    if(!condition(pair.first))
    {
        m.erase(pair.first);
    }
}

捉虫

开始代码跑起来没问题,不久出现端倪偶发报错
打断点显示在if传入condition的pair为空抛出异常
仔细反思应该是erase发生清除后map长度变短
而for继续按照初始长度进行遍历直至越界

修复

既已定位到问题,尝试改写for逻辑

for (auto it = m.begin(); it != m.end(); )
{
    if(!condition(it->first)) 
    {
        m.erase(it++); 
    }
    else
    {
        ++it;
    }
}

优雅

自C++11后map::erase(itor)方法默认返回下一个itor可以用while改写

auto it = m.begin();
while(it != m.end())
{
    if(!condition(it->first))
    {
        pair = m.erase(it);
    }
    else
    {
        ++it;
    }
}

深入

仔细思考还有些许疑惑未解
每次earse发生可能触发unordered_map内部rehashing
上述方法仅保证遍历不越界
无法避免某个itor被略过或反复操作的可能
查阅cpp reference对此作出的解释为

C++14 Iterator validity
Only the iterators and references to the elements removed are invalidated.
The rest are unaffected.
The relative order of iteration of the elements not removed by the operation is preserved.

C++11没有第三句话,故使用-std=c++14编译确保上述逻辑正确
下次遇到还是老老实实开个vector保存待清除key逐个调用erase最为稳妥吧……

参考

c++ - Problem with std::map::iterator after calling erase() - Stack Overflow
unordered_map::erase - C++ Reference

原文地址:https://www.cnblogs.com/azureology/p/15637503.html