【问题标题】:unordered_map not being updated properlyunordered_map 未正确更新
【发布时间】:2021-11-03 16:19:59
【问题描述】:

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

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

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

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

【问题讨论】:

  • 在迭代容器时从容器中擦除元素...您不能用于范围,您必须回退到迭代器。
  • 在迭代容器时修改容器可能会导致问题。我建议使用迭代器的“正常”for 循环,并注意the erase function 返回
  • 在那个范围循环中有一个“幕后”的迭代器,它被erase 无效。
  • 使用 inserterase 之类的函数时,您必须阅读文档以了解该操作的无效内容(迭代器、引用...)一个给定的容器,直到你有足够的经验来了解规则......还要检查函数返回和采样的内容。

标签: c++ segmentation-fault unordered-map


【解决方案1】:

以这种方式迭代时,您不能删除地图的元素。当您擦除迭代器时,它会失效,因此您需要在删除元素之前显式递增它。

试试这个代码:

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

【讨论】:

  • m.erase(it++) 不正确。正确的是it = m.erase(it)
  • 应该是it = m.erase(it);(减慢4秒:))
  • 是的,对不起,我还在 C++11 之前。
【解决方案2】:

其中一个 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)); 
});

【讨论】:

    猜你喜欢
    • 2021-10-26
    • 2018-01-20
    • 2020-04-09
    • 2013-04-02
    • 2017-03-09
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多