use*_*112 8 c++ unordered-map c++11
我正在将一个{string, MyStruct}对象插入到unordered_map中,稍后迭代unordered_map并选择擦除该元素.但是,在擦除元素之前,我有一个断言,它显示unordered_map为空.
这是我的插入内容:
my_umap.insert(std::make_pair(key.toString(), my_struct));
Run Code Online (Sandbox Code Playgroud)
该结构包含一个记录插入时间的成员.然后我定期检查地图并删除已经在unordered_map中的元素太长时间:
for(auto it = my_umap.begin(); it != my_umap.end(); ++it){
MyStruct& myStruct = it->second;
const bool deleteEntry = myStruct.ts.IsElapsed(std::chrono::seconds(5));
if(deleteEntry){
const string& key = it->first; // Cannot access memory for 'key'
assert(my_umap.size() >= 1); // This is failing
my_umap.erase(key);
}
}
Run Code Online (Sandbox Code Playgroud)
我在gdb中运行代码,断言失败.当我查询key它的值时说
无法访问内存
当我查询my_umap它的大小时,表示大小为零.
如果unordered_map的大小为零,for循环如何检测元素?没有其他线程访问此容器.我想unordered_map::insert()将对象复制到容器中,所以删除的原始对象应该无关紧要?
Kar*_*oll 11
调用后my_umap.erase(...),迭代器变为无效:
擦除元素的引用和迭代器无效.其他迭代器和引用不会失效.
这意味着一旦项被擦除,指向它的迭代器就不再有效.
你有几个选择:
erase()从C++ 11开始,迭代器擦除将返回一个迭代器,指向地图中的下一个项目.所以你可以使用它来保持你的迭代器有效:
auto it = my_umap.begin();
while (it != my_umap.end()) {
MyStruct& myStruct = it->second;
const bool deleteEntry = myStruct.ts.IsElapsed(std::chrono::seconds(5));
if(deleteEntry){
assert(my_umap.size() >= 1);
it = my_umap.erase(it); // <-- Return value should be a valid iterator.
}
else{
++it; // Have to manually increment.
}
}
Run Code Online (Sandbox Code Playgroud)
或者,您可以将删除候选项存储在列表对象中(例如,向量并在初始迭代后删除它们:
std::vector<MapType::iterator> deleteCandidates;
for(auto it = my_umap.begin(); it != my_umap.end(); ++it){
MyStruct& myStruct = it->second;
const bool deleteEntry = myStruct.ts.IsElapsed(std::chrono::seconds(5));
if(deleteEntry)
deleteCandidates.push_back(it);
}
for (auto it : deleteCandidates) {
my_umap.erase(it);
}
Run Code Online (Sandbox Code Playgroud)
至于你的断言失败的原因,你可能通过访问无效的迭代器遇到未定义的行为,使你的for循环认为地图仍然不是空的(因为invalidIterator != my_umap.end()).
erase()invalides你正在擦除的迭代器.当您随后在for循环中递增它时,您将获得未定义的行为.在assert()很可能触发你的循环的第二次迭代,而不是你第一次.
你必须重新调整你的循环,如:
for(auto it = my_umap.begin(); it != my_umap.end(); /* nothing */){
MyStruct& myStruct = it->second;
const bool deleteEntry = myStruct.ts.IsElapsed(std::chrono::seconds(5));
if(deleteEntry) {
// either use the return
it = my_umap.erase(it); // NB: erase by it, not by key, why
// do an extra lookup?
// or post-increment
my_umap.erase(it++);
}
else {
++it;
}
}
Run Code Online (Sandbox Code Playgroud)
就个人而言,我更喜欢it = map.erase(it)到map.erase(it++),但情况因人而异.
我只是将它包装在一个函数模板中,这样你就不必继续重写这种东西了:
template <class Container, class F>
void erase_if(Container& c, F&& f) {
for (auto it = c.begin(); it != c.end(); ) {
if (f(*it)) {
it = c.erase(it);
}
else {
++it;
}
}
}
Run Code Online (Sandbox Code Playgroud)
然后:
erase_if(my_umap, [](const auto& pr){
MyStruct& myStruct = pr.second;
return myStruct.ts.IsElapsed(std::chrono::seconds(5));
});
Run Code Online (Sandbox Code Playgroud)