在基于范围的 for 循环中获取无效引用

Getting an invalid reference in a range-based for loop

本文关键字:循环 for 获取 引用 无效 于范围 范围      更新时间:2023-10-16
auto& kphist = this->kphist;
for (auto& it : kphist) {
    it.second.aging(); // EXC-BAD-ACCESS
    if(it.second.age > LAST_DAY){
        kphist.erase(it.first);
        continue;
    }
}

KPHIST是私人会员

Class A{
private:
    unordered_map<int, KeyPointHistory> kphist;
} 

调试器显示 kphist 中的所有项目都是有效的,怎么可能在 for 循环中有一个错误的引用。可能出现什么问题?

从 cppreference.com for std::unordered_map::erase()对擦除元素的引用和迭代器无效。其他迭代器和引用不会失效。因此,您不能使用 for 循环范围内的std::unordered_map::erase()(因为这将尝试递增无效的迭代器)。

为避免递增无效的迭代器,您可以先递增即可然后使用原始迭代器擦除:

for(auto i=map.begin(),end=map.end(); i!=end; ) { // no increment here
  auto it=i++;                                    // but here instead
  if(must_remove(it))
    map.erase(it);
}

事实上,由于erase()将迭代器返回到下一个元素,因此您可以避免额外的迭代器it(感谢 Hurkyl 在注释中指出这一点):

for(auto i=map.begin(),end=map.end(); i!=end; ) { // no increment here
  if(must_remove(i))
    i = map.erase(i);                             // but here 
  else
    ++i;                                          //  or here instead
}

无需列出要擦除的元素键...

顺便说一句,你为什么不使用std::map(而不是std::unordered_map),因为你的密钥是一个int(很容易订购)?另外,为什么要对同名的成员变量进行引用kphist

迭代内容/迭代器时,您不能删除它,也不应该。

将元素索引保存在不同的容器中,完成后循环遍历并擦除您获得的元素。

可能出现什么问题?

万事!

您可以通过直接将迭代器传递给要擦除的项目来擦除unordered_map。执行此操作时,erase()返回后续迭代器,因此您可以执行以下操作:

for (auto pos = kphist.begin(); pos != kphist.end(); ) {
       it.second.aging();
       if(it.second.age > LAST_DAY)
           pos = kphist.erase(it); 
       else
           ++pos;
}

作为奖励,这可能比传递要擦除的密钥快一点 - 由于您提供了迭代器,因此可以直接到达要擦除的项目,而不是重新散列密钥以找到您已经知道的位置。