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

fgw7neuy  于 2023-03-25  发布在  其他
关注(0)|答案(2)|浏览(180)

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

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

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

完整代码如下:

#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
*/

我不明白为什么它不能遍历整个无序Map。
此外,不确定这是否有助于清晰的图像,但是,当使用标准Map而不是无序Map时,它会在同一示例中给出地址边界错误,其中Map的下一个字符需要像这样更新:

eqqqjvef

eqqqjvef1#

C++的一个陷阱是迭代器在被修改时对大多数容器无效。
std::unordered_map<Key,T,Hash,KeyEqual,Allocator>::erase - cppreference.com
对被擦除元素的引用和迭代器无效,其他迭代器和引用不会无效。
所以当你从m中删除项时,当前迭代器就变得无效了。
现在range base for loop uses iterators underneath
最好的解决方法是使用std::erase_if算法:

std::erase_if(m.begin(), m.end(), [](const auto& i) { 
    return !(std::islower(i.first)); 
});
2lpgd968

2lpgd9682#

在这种方式迭代时,你不能擦除map的元素。当你擦除迭代器时,它将变得无效,所以你需要在删除元素之前显式地递增它。
请尝试以下代码:

for (auto it = m.begin(); it != m.end();)
     if (!((*it).first >= 'a' && (*it).first <= 'z'))
         it = m.erase(it);
     else
        ++it;

相关问题