Como remover de um mapa enquanto iterar?

Como faço para remover de um mapa enquanto iterar? gostar:

std::map map; for(auto i : map) if(needs_removing(i)) // remove it from the map 

Se eu usar map.erase isso invalidará os iteradores

    O idioma de exclusão de contêiner associativo padrão:

     for (auto it = m.cbegin(); it != m.cend() /* not hoisted */; /* no increment */) { if (must_delete) { m.erase(it++); // or "it = m.erase(it)" since C++11 } else { ++it; } } 

    Note que nós realmente queremos um loop for normal aqui, já que estamos modificando o próprio container. O loop baseado em intervalo deve ser estritamente reservado para situações em que apenas nos preocupamos com os elementos. A syntax da RBFL deixa isso claro ao não expor o contêiner dentro do corpo do loop.

    Editar. Pré-C ++ 11, você não podia apagar os constantes-iteradores. Lá você teria que dizer:

     for (std::map::iterator it = m.begin(); it != m.end(); ) { /* ... */ } 

    Apagar um elemento de um contêiner não está em desacordo com a constância do elemento. Por analogia, sempre foi perfeitamente legítimo delete p onde p é um ponteiro para constante. Constness não restringe a vida; valores const em C ++ ainda podem parar de existir.

    Eu pessoalmente prefiro esse padrão que é um pouco mais claro e simples, às custas de uma variável extra:

     for (auto it = m.cbegin(), next_it = m.cbegin(); it != m.cend(); it = next_it) { next_it = it; ++next_it; if (must_delete) { m.erase(it); } } 

    Vantagens dessa abordagem:

    • o incrementador de loop for faz sentido como um incrementador;
    • a operação de apagar é um simples apagamento, em vez de ser misturado com a lógica de incremento;
    • após a primeira linha do corpo do loop, o significado it e next_it permanece fixo durante toda a iteração, permitindo que você inclua facilmente instruções adicionais referindo-se a elas sem se preocupar se funcionarão como pretendido (exceto, é claro, que você não pode usá- it depois apagando – o).

    Em suma “Como faço para remover de um mapa enquanto iterar?”

    • Com o mapa antigo impl: Você não pode
    • Com o novo mapa impl: quase como @KerrekSB sugerido. Mas existem alguns problemas de syntax no que ele postou.

    Do mapa do GCC impl (nota GXX_EXPERIMENTAL_CXX0X ):

     #ifdef __GXX_EXPERIMENTAL_CXX0X__ // _GLIBCXX_RESOLVE_LIB_DEFECTS // DR 130. Associative erase should return an iterator. /** * @brief Erases an element from a %map. * @param position An iterator pointing to the element to be erased. * @return An iterator pointing to the element immediately following * @a position prior to the element being erased. If no such * element exists, end() is returned. * * This function erases an element, pointed to by the given * iterator, from a %map. Note that this function only erases * the element, and that if the element is itself a pointer, * the pointed-to memory is not touched in any way. Managing * the pointer is the user's responsibility. */ iterator erase(iterator __position) { return _M_t.erase(__position); } #else /** * @brief Erases an element from a %map. * @param position An iterator pointing to the element to be erased. * * This function erases an element, pointed to by the given * iterator, from a %map. Note that this function only erases * the element, and that if the element is itself a pointer, * the pointed-to memory is not touched in any way. Managing * the pointer is the user's responsibility. */ void erase(iterator __position) { _M_t.erase(__position); } #endif 

    Exemplo com estilo antigo e novo:

     #include  #include  #include  #include  using namespace std; typedef map t_myMap; typedef vector t_myVec; int main() { cout < < "main() ENTRY" << endl; t_myMap mi; mi.insert(t_myMap::value_type(1,1)); mi.insert(t_myMap::value_type(2,1)); mi.insert(t_myMap::value_type(3,1)); mi.insert(t_myMap::value_type(4,1)); mi.insert(t_myMap::value_type(5,1)); mi.insert(t_myMap::value_type(6,1)); cout << "Init" << endl; for(t_myMap::const_iterator i = mi.begin(); i != mi.end(); i++) cout << '\t' << i->first < < '-' << i->second < < endl; t_myVec markedForDeath; for (t_myMap::const_iterator it = mi.begin(); it != mi.end() ; it++) if (it->first > 2 && it->first < 5) markedForDeath.push_back(it->first); for(size_t i = 0; i < markedForDeath.size(); i++) // old erase, returns void... mi.erase(markedForDeath[i]); cout << "after old style erase of 3 & 4.." << endl; for(t_myMap::const_iterator i = mi.begin(); i != mi.end(); i++) cout << '\t' << i->first < < '-' << i->second < < endl; for (auto it = mi.begin(); it != mi.end(); ) { if (it->first == 5) // new erase() that returns iter.. it = mi.erase(it); else ++it; } cout < < "after new style erase of 5" << endl; // new cend/cbegin and lambda.. for_each(mi.cbegin(), mi.cend(), [](t_myMap::const_reference it){cout << '\t' << it.first << '-' << it.second << endl;}); return 0; } 

    impressões:

     main() ENTRY Init 1-1 2-1 3-1 4-1 5-1 6-1 after old style erase of 3 & 4.. 1-1 2-1 5-1 6-1 after new style erase of 5 1-1 2-1 6-1 Process returned 0 (0x0) execution time : 0.021 s Press any key to continue. 

    Muito triste, né? A maneira que eu costumo fazer isso é construir um contêiner de iteradores em vez de excluir durante a travessia. Em seguida, percorra o contêiner e use map.erase ()

     std::map map; std::list< std::map::iterator > iteratorList; for(auto i : map ){ if ( needs_removing(i)){ iteratorList.push_back(i); } } for(auto i : iteratorList){ map.erase(*i) } 

    Assumindo C ++ 11, aqui está um corpo de loop de uma linha, se isso for consistente com seu estilo de programação:

     using Map = std::map; Map map; // Erase members that satisfy needs_removing(itr) for (Map::const_iterator itr = map.cbegin() ; itr != map.cend() ; ) itr = needs_removing(itr) ? map.erase(itr) : std::next(itr); 

    Um par de outras alterações de estilo menores:

    • Mostra o tipo declarado ( Map::const_iterator ) quando possível / conveniente, usando auto .
    • Use using para tipos de modelo, para facilitar a leitura / manutenção de tipos auxiliares ( Map::const_iterator ).