天天看點

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

c++

繼續閱讀