在阅读了所有其他答案后,我在这里处于优势......但它就在这里。
但是它调用的函数是可能的->second(this);从 translationEvents 地图中移除一个元素(通常是它自己)
如果这是真的,也就是说,回调可以从容器中删除 任何 元素,那么您不可能从循环本身解决这个问题。
删除当前回调
在回调只能删除自身的更简单情况下,您可以使用不同的方法:
// [1] Let the callback actually remove itself
for ( iterator it = next = m.begin(); it != m.end(); it = next ) {
++next;
it->second(this);
}
// [2] Have the callback tell us whether we should remove it
for ( iterator it = m.begin(); it != m.end(); ) {
if ( !it->second(this) ) { // false means "remove me"
m.erase( it++ );
} else {
++it;
}
}
在这两个选项中,我显然更喜欢 [2],因为您将回调与处理程序的实现分离。也就是说,[2] 中的回调对保存它的容器一无所知。 [1] 具有更高的耦合度(回调知道容器)并且更难推理,因为容器从代码中的多个位置更改。一段时间后,您甚至可能会回顾代码,认为这是一个奇怪的循环(不记得回调会自行删除)并将其重构为更明智的为for ( auto it = m.begin(), end = m.end(); it != end; ++it ) it->second(this);
删除其他回调
对于更复杂的问题可以移除任何其他回调,这一切都取决于你可以做出的妥协。在简单的情况下,它只在完成迭代之后删除其他回调,您可以提供一个单独的成员函数来保留要删除的元素,然后在循环完成后立即将它们全部删除:
void removeElement( std::string const & name ) {
to_remove.push_back(name);
}
...
for ( iterator it = m.begin(); it != m.end(); ++it ) {
it->second( this ); // callback will possibly add the element to remove
}
// actually remove
for ( auto it = to_remove.begin(); it != to_begin.end(); ++it ) {
m.erase( *it );
}
如果需要立即删除元素(即,如果尚未调用它们,即使在此迭代中也不应调用它们),那么您可以通过在执行调用之前检查它是否标记为删除来修改该方法.标记可以通过两种方式完成,其中通用的是将容器中的值类型更改为pair<bool,T>,其中 bool 表示它是否存在。如果在这种情况下,可以更改包含的对象,您可以这样做:
void removeElement( std::string const & name ) {
auto it = m.find( name ); // add error checking...
it->second = TranslationFinished(); // empty functor
}
...
for ( auto it = m.begin(); it != m.end(); ++it ) {
if ( !it->second.empty() )
it->second(this);
}
for ( auto it = m.begin(); it != m.end(); ) { // [3]
if ( it->second.empty() )
m.erase( it++ );
else
++it;
}
请注意,由于回调可以删除容器中的 任何 元素,因此您不能随手擦除,因为当前回调可以删除一个已经访问过的迭代器。再说一次,你可能暂时不关心空函子,所以可以忽略它并在你去的时候执行erase。已访问且标记为删除的元素将在下一轮中清除。