我有一个std::map
和一个std::unordered_set
,具有相同的密钥。
我要从集合中删除地图中不存在的所有键。
我的想法是执行以下操作:
#include <map>
#include <unordered_set>
int main()
{
std::map<uint64_t, std::string> myMap = {
{1, "foo"},
{2, "bar"},
{3, "morefoo"},
{4, "morebar"}
};
std::unordered_set<uint64_t> mySet = { 1, 2, 3, 4, 123 };
for (const auto key : mySet)
{
const auto mapIterator = myMap.find(key);
if (myMap.end() == mapIterator)
mySet.erase(key);
}
return 0;
}
然后每隔几秒钟在计时器回调中调用它,但是,当试图从123
删除密钥mySet
时,上面的代码段抛出一个断言,说明:
列表迭代器不可递增。
问题是,即使它没有引发异常,我也觉得这个想法远非优雅/最优。我想知道是否有更好的方法来解决这个问题?
谢谢。
答案 0 :(得分:2)
如评论中所述
for (const auto key : mySet)
{
const auto mapIterator = myMap.find(key);
if (myMap.end() == mapIterator)
mySet.erase(key);
}
将具有未定义的行为。从123
中删除元素mySet
时,会使基于范围的for循环使用的迭代器无效。执行完该操作后,不允许递增该迭代器。您可以做的是切换到常规的for循环,以便可以控制迭代器何时递增,如
for (auto it = mySet.begin(); it != mySet.end();)
{
if (myMap.find(*it) == myMap.end())
it = mySet.erase(it);
else
++it;
}
现在您将始终拥有一个有效的迭代器,因为erase
将返回下一个有效的迭代器。
答案 1 :(得分:2)
如对问题How to remove from a map while iterating it?的回答所述,在for范围循环中对其进行迭代时,您无法擦除容器中的元素,因此您的代码应使用迭代器。这样的答案已经提供了,我不会重复。
为了提高效率,您有一种非常错误的方法-您在std::map
的同时进行std::unordered_set
的查找,这与std::unorederd_set
可以提供更快的查找时间相反(否则就没有意义了)因为您可以使用它代替std::set
)。一种可能的方法是创建副本:
auto copySet = mySet;
for( const auto &p : myMap )
copySet.erase( p.first );
for( const auto v : copySet )
mySet.erase( v );
这可能会更有效,但这取决于您的数据。为容器选择合适的数据类型的更好方法。
注意:通过错误的方法,我的意思是仅针对您所提出的问题的这种效率,但这似乎是更大程序的一部分,并且此解决方案可能是正确的,因为在某些情况下,当此数据结构效果很好。