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

C++ 在基于范围的for循环中获取无效引用,c++,c++11,for-loop,C++,C++11,For Loop,kphist是一个私人会员 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; } } A类{ 私人: 无序地图kphist; } 调试器显示kp

kphist是一个私人会员

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;
    }

}
A类{
私人:
无序地图kphist;
} 

调试器显示kphist中的所有项都是有效的,为什么可能在for循环中存在错误引用。有什么可能出错?

在对内容/迭代器进行迭代时,您不能删除它,也不应该删除它

将元素索引保存在另一个容器中,完成后循环遍历并删除得到的元素

有什么可能出错


一切

在对内容/迭代器进行迭代时,不能删除它,也不应删除

将元素索引保存在另一个容器中,完成后循环遍历并删除得到的元素

有什么可能出错

一切

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

为了避免增加无效迭代器,只需先增加 然后使用原始迭代器擦除:

Class A{
private:
    unordered_map<int, KeyPointHistory> kphist;
} 
事实上,由于
erase()
将迭代器返回到下一个元素,因此可以避免使用额外的迭代器
it
(感谢Hurkyl在注释中指出这一点):

不需要列出要擦除的元素的键

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

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

为了避免增加无效迭代器,只需先增加 然后使用原始迭代器擦除:

Class A{
private:
    unordered_map<int, KeyPointHistory> kphist;
} 
事实上,由于
erase()
将迭代器返回到下一个元素,因此可以避免使用额外的迭代器
it
(感谢Hurkyl在注释中指出这一点):

不需要列出要擦除的元素的键


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

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

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
}

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

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

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
}


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

什么是
KeyPointHistory
?请注意
std::unordered\u map::erase
:未擦除元素的顺序将保留(这使得在遍历容器时可以擦除单个元素)(从C++14开始)在对映射进行迭代时,不要修改它。@chris我很确定,首先删除当前迭代器,然后尝试递增它仍然不合法。@Brian,当然了,页面指出迭代器无效。但即使保持迭代器有效,仍然存在一个问题。
KeyPointHistory
是什么p参考
std::unordered_map::erase
:保留未擦除元素的顺序(这使得在遍历容器时可以擦除单个元素)(从C++14开始)在对映射进行迭代时,不要修改它。@chris我很确定,首先删除当前迭代器,然后尝试递增它仍然是不合法的。@Brian,当然,页面注意到迭代器无效。但即使保持迭代器有效,仍然存在一个问题。不需要保存ele的索引要擦除并稍后擦除的元素。可以在一个循环中完成所有操作。请参见我的答案。无需保存要擦除并稍后擦除的元素索引。可以在一个循环中完成所有操作。请参见我的答案。看起来合法,但如此丑陋:)@deW1美丽在旁观者的眼中。为什么这么难看?这很实用。你能得到一个简短的版本吗?(或者你如何定义这里的美?)。请注意,基于范围的for循环看起来简洁(漂亮),但实际上在转换为普通代码时非常复杂(因此很难看)。顺便说一句,这肯定比你的建议好。我一直认为这样做是因为
if(remove){I=map.erase(I);}else{++I;}
。这种风格还有一个优点,就是它适用于所有迭代器在修改后都会失效的容器。您的代码非常好,但在我看来,a
for(auto a:b)
在视觉上更漂亮,但当然并非总是适用于我们所有人know@Hurkyl是的,这也是可能的,事实上避免了额外的迭代器。我忘记了
erase()
将迭代器返回到下一个元素。我会修正我的答案。似乎是正确的,但如此丑陋:)美丽在人的眼里