Bagaimana cara saya menghapus dari peta saat iterasi? Suka:
std::map<K, V> map;
for(auto i : map)
if(needs_removing(i))
// remove it from the map
Jika saya menggunakannya map.erase
akan membatalkan iterators
Bagaimana cara saya menghapus dari peta saat iterasi? Suka:
std::map<K, V> map;
for(auto i : map)
if(needs_removing(i))
// remove it from the map
Jika saya menggunakannya map.erase
akan membatalkan iterators
Jawaban:
Ungkapan asosiasi asosiatif-wadah standar:
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;
}
}
Perhatikan bahwa kami benar-benar menginginkan for
loop biasa di sini, karena kami memodifikasi wadah itu sendiri. Lingkaran berbasis rentang harus dicadangkan secara ketat untuk situasi di mana kita hanya peduli pada elemen. Sintaks untuk RBFL membuat ini jelas dengan bahkan tidak mengekspos wadah di dalam tubuh loop.
Edit. Pra-C ++ 11, Anda tidak bisa menghapus const-iterators. Di sana Anda harus mengatakan:
for (std::map<K,V>::iterator it = m.begin(); it != m.end(); ) { /* ... */ }
Menghapus elemen dari wadah tidak bertentangan dengan ketegasan elemen. Dengan analogi, itu selalu sangat sah di delete p
mana p
pointer-ke-konstan. Kekekalan tidak membatasi hidup; nilai const di C ++ masih bisa berhenti ada.
for (int i = 0; i < v.size(); i++)
. Di sini kita harus mengatakan v[i]
di dalam loop, yaitu kita harus secara eksplisit menyebutkan wadah. RBFL di sisi lain memperkenalkan variabel loop yang langsung dapat digunakan sebagai nilai, dan jadi tidak ada pengetahuan tentang kontainer yang diperlukan di dalam loop. Ini adalah petunjuk penggunaan RBFL yang dimaksudkan untuk loop yang tidak perlu tahu tentang wadah. Menghapus adalah situasi yang sangat berlawanan, di mana itu semua tentang wadah.
it
untuk mendapatkan iterator berikutnya yang valid, dan kemudian menghapus yang lama. Itu tidak bekerja sebaliknya!
it = v.erase(it);
sekarang berfungsi untuk peta juga. Artinya, hapus () pada semua elemen asosiatif sekarang mengembalikan iterator berikutnya. Jadi kludge lama yang membutuhkan kenaikan pasca ++ dalam delete (), tidak diperlukan lagi. Ini (jika benar) adalah Good Thing, karena kludge mengandalkan sihir pasca-kenaikan-timpa-dalam-fungsi-panggilan, "diperbaiki" oleh pengelola pemula untuk mengambil kenaikan dari panggilan fungsi, atau untuk menukarnya ke preincrement "karena itu hanya hal gaya", dll.
it++
dalam if
dan else
blok? bukankah itu cukup untuk menyebutnya sekali setelah ini?
Saya pribadi lebih suka pola ini yang sedikit lebih jelas dan sederhana, dengan mengorbankan variabel tambahan:
for (auto it = m.cbegin(), next_it = it; it != m.cend(); it = next_it)
{
++next_it;
if (must_delete)
{
m.erase(it);
}
}
Keuntungan dari pendekatan ini:
it
dan next_it
tetap tetap di seluruh iterasi, memungkinkan Anda untuk dengan mudah menambahkan pernyataan tambahan merujuk mereka tanpa headscratching apakah mereka akan berfungsi sebagaimana dimaksud (kecuali tentu saja bahwa Anda tidak dapat menggunakan it
setelah menghapusnya) .Singkatnya, "Bagaimana saya menghapus dari peta saat iterasi?"
Dari imp peta GCC (perhatikan 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
Contoh dengan gaya lama dan baru:
#include <iostream>
#include <map>
#include <vector>
#include <algorithm>
using namespace std;
typedef map<int, int> t_myMap;
typedef vector<t_myMap::key_type> 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;
}
cetakan:
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.
mi.erase(it++);
?
if(mi.empty()) break;
.
Draf C ++ 20 berisi fungsi kenyamanan std::erase_if
.
Jadi Anda bisa menggunakan fungsi itu untuk melakukannya sebagai satu-liner.
std::map<K, V> map_obj;
//calls needs_removing for each element and erases it, if true was reuturned
std::erase_if(map_obj,needs_removing);
//if you need to pass only part of the key/value pair
std::erase_if(map_obj,[](auto& kv){return needs_removing(kv.first);});
Cukup sedih, kan? Cara saya biasanya melakukannya adalah membangun wadah iterator bukannya menghapus selama traversal. Kemudian putar melalui wadah dan gunakan map.erase ()
std::map<K,V> map;
std::list< std::map<K,V>::iterator > iteratorList;
for(auto i : map ){
if ( needs_removing(i)){
iteratorList.push_back(i);
}
}
for(auto i : iteratorList){
map.erase(*i)
}
Dengan asumsi C ++ 11, di sini adalah badan loop satu-liner, jika ini konsisten dengan gaya pemrograman Anda:
using Map = std::map<K,V>;
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);
Beberapa perubahan gaya minor lainnya:
Map::const_iterator
) bila memungkinkan / nyaman, selama digunakan auto
.using
untuk tipe template, untuk membuat tipe tambahan ( Map::const_iterator
) lebih mudah dibaca / dipelihara.