关于这里提供的答案:如何使用反向迭代器调用erase
下面的代码,当在g++ 4.8.4中使用 -std=c++11编译时,会导致段错误(在++it
处)。我是否误解了答案?
std::map<int,int> testmap;
testmap[0] = 1;
for(auto it=testmap.rbegin(); it!=testmap.rend(); ++it) {
testmap.erase( std::next(it).base() );
}
关于这里提供的答案:如何使用反向迭代器调用erase
下面的代码,当在g++ 4.8.4中使用 -std=c++11编译时,会导致段错误(在++it
处)。我是否误解了答案?
std::map<int,int> testmap;
testmap[0] = 1;
for(auto it=testmap.rbegin(); it!=testmap.rend(); ++it) {
testmap.erase( std::next(it).base() );
}
erase
会使迭代器失效,因此您必须从 erase
的返回值重新构造迭代器:
it = std::map<int,int>::reverse_iterator(testmap.erase( std::next(it).base() ));
或使用C++11:
it = decltype(it){testmap.erase( std::next(it).base() )};
或者使用C++17:
it = std::reverse_iterator(testmap.erase( std::next(it).base() ));
演示。
为了完整起见,这是原始问题中已更正的循环的样子(请注意,迭代器增量已从for(...)
中删除):
for (auto rit = testmap.rbegin(); rit != testmap.rend(); /* empty */) {
if (WE_WANT_TO_ERASE(rit)) {
rit = decltype(rit){ testmap.erase(std::next(rit).base()) };
} else {
++rit;
}
}
erase
函数,请记得跳过++it
,否则可能会漏掉一个元素。 - Quentin{reverse_iterator(v.end()), reverse_iterator(v.begin())}
是范围{v.rbegin(), v.rend()}
。(实际上,它的内部应该指向一个有效的范围{v.begin(), v.end()}
) - Jarod42erase
指向被删除元素之后的下一个元素,而在反向迭代中,下一个元素之前的元素是下一个元素。 - Yakk - Adam Nevraumont在使用这个习语一段时间后,我认为需要对Jarod42答案中的循环进行修改,以使事情更加安全,并保持典型的for(;;)
循环的美好特性:
for (auto it = testcont.rbegin(), nit = it; it != testcont.rend(); it = nit) {
nit = next(it);
// whatever... maybe a continue somewhere or maybe not
if (WE_WANT_TO_ERASE(it)) {
nit = decltype(it){ testcont.erase(std::next(it).base()) };
}
// whatever... maybe a continue somewhere or maybe not
}
continue;
,而没有先增加迭代器,结果将是一个无限循环。由于乍一看,这可能看起来像一个普通的 for(;;)
循环,我相信这迟早会发生。同样,如果循环中有分支,并且其中一个分支忽略了增加迭代器,则会引入另一个错误。最后,如果你执行了一个 erase()
,你需要确保在增加迭代器之前 continue
,否则你又有另一个错误。for(;;)
循环一样处理。诀窍是在循环体的第一行递增 nit
("下一个迭代器")。然后你就不用担心了。唯一需要更新 nit
的时候是当你执行一个 erase()
。其他所有操作都像正常的 for 循环一样工作。vector
、list
等也适用。
it = decltype(it){testmap.erase( std::next(it).base() )};
替代++it
。 - Jarod42it.base()
-- 对应于反向迭代器的“正常”迭代器。 - Quentinerase
会使你传入的迭代器失效,并返回你应该继续遍历的迭代器。 - Quentin