Прочитав все остальные ответы, я нахожусь в преимуществе здесь ... Но здесь это идет.
Однако это возможно для функции, вызываемой it-> 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
по ходу дела. Уже посещенные элементы, помеченные для удаления, будут удалены в следующем проходе.