在 for 循环中修改 std::unordered_map 时,未正确更新它

hru*_*uja 2 c++ unordered-map segmentation-fault

尝试使用以下代码片段更新无序映射,使其仅包含小写字母,但它似乎在删除一个键值对 { [33 '!']: 3 } 后停止,并退出循环,使映射的其余部分未被访问并打印部分更新的地图。

 for (auto &i : m)
        if (!(i.first >= 'a' && i.first <= 'z'))
            m.erase(i.first);
Run Code Online (Sandbox Code Playgroud)

以下调试图像揭示了上述内容

在此输入图像描述

在此输入图像描述

完整代码如下:

#include <iostream>
#include <unordered_map>
#include <algorithm>    
using namespace std;
int main()
{
    string line = "Try! Try! Try! until you succeed";
    //getline(cin, line);
    unordered_map<char, int> m;
    for (int i = 0; line[i]; i++)
    {   
        char lower = (char)tolower(line[i]);
        if (m.find(lower) == m.end())
            m.insert(make_pair(lower, 1));
        else
            m[lower]++;
    }

    for (auto &i : m) //only updates until ! 
        if (!(i.first >= 'a' && i.first <= 'z'))
            m.erase(i.first);

    cout<<"The freq. map so formed is : \n";
    for (auto &i : m)
        cout<<i.first<<"\t"<<i.second<<endl;
    
    return 0;
}
/*
OUTPUT : 
The freq. map so formed is : 
d       1
t       4
r       3
e       2
y       4
l       1
o       1
        5
n       1
u       3
i       1
s       1
c       2
*/
Run Code Online (Sandbox Code Playgroud)

似乎无法理解为什么它不会循环遍历完整的无序地图。

另外,不确定这是否有助于获得清晰的图片,但是,当使用标准地图而不是无序地图时,它会在需要更新地图的下一个字符的同一实例中给出地址边界错误,如下所示:

在此输入图像描述

在此输入图像描述

Mar*_*k R 5

C++ 陷阱之一是,大多数容器的迭代器在修改时都会失效。

std::unordered_map<Key,T,Hash,KeyEqual,Allocator>::erase - cppreference.com

对已擦除元素的引用和迭代器无效。其他迭代器和引用不会失效。

因此,当您从表单中删除项目时,m当前迭代器将变得无效。

现在范围基础 for 循环使用下面的迭代器

解决这个问题的最佳方法是使用std::erase_if算法:

std::erase_if(m.begin(), m.end(), [](const auto& i) { 
    return !(std::islower(i.first)); 
});
Run Code Online (Sandbox Code Playgroud)