Bir haritayı tekrarlarken nasıl kaldırabilirim? sevmek:
std::map<K, V> map;
for(auto i : map)
if(needs_removing(i))
// remove it from the map
Eğer kullanırsam map.erase
yineleyicileri geçersiz kılacaktır
Bir haritayı tekrarlarken nasıl kaldırabilirim? sevmek:
std::map<K, V> map;
for(auto i : map)
if(needs_removing(i))
// remove it from the map
Eğer kullanırsam map.erase
yineleyicileri geçersiz kılacaktır
Yanıtlar:
Standart ilişkisel kap silme deyimi:
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;
}
}
for
Konteynerin kendisini değiştirdiğimiz için burada gerçekten sıradan bir döngü istediğimizi unutmayın . Aralık tabanlı döngü, yalnızca öğeleri önemsediğimiz durumlar için kesinlikle ayrılmalıdır. RBFL sözdizimi, kabı döngü gövdesinin içinde bile açığa çıkarmadan bunu netleştirir.
Düzenle. C ++ 11 öncesi, sabit yineleyicileri silemediniz. Orada şunu söylemelisin:
for (std::map<K,V>::iterator it = m.begin(); it != m.end(); ) { /* ... */ }
Bir öğeyi kaptan silmek, öğenin sabitliği ile çelişmez. Benzetmeyle, bir işaretçi-sabitin delete p
nerede p
olduğu her zaman meşru olmuştur . Constness kullanım ömrünü kısıtlamaz; C ++ 'da const değerleri var olanı durdurabilir.
for (int i = 0; i < v.size(); i++)
. Burada v[i]
döngü içinde söylemeliyiz , yani konteynırdan açıkça bahsetmeliyiz. Öte yandan RBFL, değer olarak doğrudan kullanılabilen döngü değişkenini sunar ve bu nedenle döngü içinde kap hakkında bilgi gerekmez. Bu, konteyner hakkında bilmek zorunda olmayan döngüler için RBFL'nin amaçlanan kullanımı için bir ipucudur . Silme, tamamen kapsayıcıyla ilgili tam tersi durumdur.
it
ve sonra eskisini silmek. Diğer şekilde çalışmaz!
it = v.erase(it);
şimdi, üzerindeki silme () 'dir too.That haritaları için çalışır tüm ilişkisel elemanları şimdi bir sonraki yineleyici döndürür. Bu nedenle, delete () içinde bir artım sonrası ++ gerektiren eski çamur artık gerekli değildir. Bu, (eğer doğruysa) iyi bir şeydir, çünkü çamur, işlev çağrısı çağrısındaki arttırma sonrası büyü artışına güvendiğinden, yeni başlayanlar tarafından işlev çağrısından artışı almak veya değiştirmek için "sabitlendi" "çünkü bu sadece tarz bir şey", vb.
it++
içinde if
ve else
blokların? bunlardan sonra bir kez aramak yeterli olmaz mı?
Ben şahsen ekstra değişken pahasına, biraz daha açık ve basit olan bu kalıbı tercih ederim:
for (auto it = m.cbegin(), next_it = it; it != m.cend(); it = next_it)
{
++next_it;
if (must_delete)
{
m.erase(it);
}
}
Bu yaklaşımın avantajları:
it
ve next_it
sabit kalması, amaçlanan şekilde çalışıp çalışmayacaklarına dair kafa çizmeden onlara atıfta bulunan ek ifadeler kolayca eklemenize izin verir (elbette it
sildikten sonra kullanamayacağınız hariç ) .Kısacası "Bir haritayı tekrarlarken nasıl kaldırabilirim?"
GCC harita göstergesinden (not 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
Eski ve yeni stile örnek:
#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;
}
baskılar:
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;
.
C ++ 20 taslağı uygunluk işlevini içerir std::erase_if
.
Böylece bu işlevi tek astar olarak yapmak için kullanabilirsiniz.
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);});
Çok üzücü, ha? Genelde yaptığım gibi, geçiş sırasında silmek yerine yineleyicilerden oluşan bir kap oluşturmaktır. Ardından kapsayıcıdan geçirin ve map.erase () kullanın
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)
}
C ++ 11 varsayarsak, programlama stilinizle tutarlıysa, tek katmanlı bir döngü gövdesi:
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);
Birkaç diğer küçük stil değişikliği:
Map::const_iterator
Mümkün / uygun olduğunda, üzerinde kullanarak beyan edilen türü ( ) göster auto
.using
Yardımcı türlerin ( Map::const_iterator
) okunmasını / bakımını kolaylaştırmak için şablon türleri için kullanın .