2012-03-01 12 views
12

Quindi, ho problemi con l'algoritmo std :: map, lambda e stl (remove_if). In realtà, lo stesso codice con std :: list o std :: vector funziona bene.map, lambda, remove_if

Il mio esempio di prova: messaggio

#include <map> 
#include <iostream> 
#include <algorithm> 

struct Foo 
{ 
    Foo() : _id(0) {} 
    Foo(int id) : _id(id) 
    { 

    } 

    int _id;  
}; 
typedef std::map<int, Foo> FooMap; 


int main() 
{ 
    FooMap m; 
    for (int i = 0; i < 10; ++i) 
     m[i + 100] = Foo(i); 

    int removeId = 6; 
    // <<< Error here >>> 
    std::remove_if(m.begin(), m.end(), [=](const FooMap::value_type & item) { return item.second._id == removeId ;}); 

    for (auto & item : m) 
     std::cout << item.first << " = " << item.second._id << "\n";  

    return 0; 
} 

Errore:

In file included from /usr/include/c++/4.6/utility:71:0, 
       from /usr/include/c++/4.6/algorithm:61, 
       from main.cxx:1: 
/usr/include/c++/4.6/bits/stl_pair.h: In member function ‘std::pair<_T1, _T2>& std::pair<_T1, _T2>::operator=(std::pair<_T1, _T2>&&) [with _T1 = const int, _T2 = Foo, std::pair<_T1, _T2> = std::pair<const int, Foo>]’: 
/usr/include/c++/4.6/bits/stl_algo.h:1149:13: instantiated from ‘_FIter std::remove_if(_FIter, _FIter, _Predicate) [with _FIter = std::_Rb_tree_iterator<std::pair<const int, Foo> >, _Predicate = main()::<lambda(const value_type&)>]’ 
main.cxx:33:114: instantiated from here 
/usr/include/c++/4.6/bits/stl_pair.h:156:2: error: assignment of read-only member ‘std::pair<const int, Foo>::first’ 

non capisco cosa c'è che non va qui. Quindi, sono lieto di leggere alcuni consigli/indicazioni su di esso. Il mio obiettivo: utilizzare un nuovo stile lambda con std :: map e algoritmi, come remove_if.

g ++ 4.6, -std = C++ 0x.

+2

'remove_if' accetta una coppia di iteratori e restituisce un iteratore. Dove pensi che rimuova gli elementi ** da **? –

risposta

27

Il problema è che std::map<K,V>::value_type è std::pair<const K, V>, alias .first è const e non assegnabile. Lambdas non ha nulla a che fare con il problema qui.

std::remove_if "rimuove" elementi spostando gli elementi del contenitore in modo che tutto ciò che non si adatta al predicato sia nella parte anteriore, prima dell'iteratore restituito. Tutto dopo questo iteratore non è specificato. Lo fa con un compito semplice e dato che non puoi assegnare ad una variabile const, ottieni questo errore.

Il nome remove può essere un po 'fuorviante e in questo caso, si vuole veramente erase_if, ma ahimè, che non esiste. Dovrete fare che fare con l'iterazione di tutti gli oggetti e li cancellare a mano con map.erase(iterator):

for(auto it = map.begin(), ite = map.end(); it != ite;) 
{ 
    if(it->second._id == remove_id) 
    it = map.erase(it); 
    else 
    ++it; 
} 

Questo è sicuro, perché è possibile cancellare i singoli nodi dell'albero senza gli altri iteratori sempre invalidati. Si noti che non ho incrementato l'iteratore nell'intestazione del ciclo for stesso, poiché questo saltava un elemento nel caso in cui si cancella un nodo.


† A questo punto, si dovrebbe aver notato che questo sarebbe devastare in ordine s' il std::map, che è il motivo per cui la chiave è const - quindi non è possibile influenzare l'ordine in qualsiasi modo dopo un l'articolo è stato inserito

+0

Grazie per la risposta. Quindi, c'è un modo elegante per rimuovere elementi da std :: map senza codice brutto: 'void removeFromMap (FooMap & m, int id) { \t per (auto it = m.begin(), fine = m. end();! it = fine; ++ esso) \t \t { \t \t se (it-> second._id == id) \t \t \t { \t \t \t m.cancellalo); \t \t \t interruzione; \t \t} \t} } ' – Reddy

+0

@Reddy: No, nessun altro modo di cui sono a conoscenza. A proposito, se i tuoi ID non sono univoci, cancellerai solo il primo elemento nella mappa. Se lo sono, allora quel ciclo va bene. – Xeo

+0

Sì, lo so. – Reddy

3

È possibile utilizzare Trova e cancella per la mappa. Non è conveniente come remove_if, ma potrebbe essere il migliore che hai.

int removeId = 6; 
auto foundIter = m.find(removeId); 

// if removeId is not found you will get an error when you try to erase m.end() 
if(foundIter != m.end()) 
{ 
    m.erase(foundIter); 
}