2012-03-01 20 views
12

Tengo un problema con los algoritmos std :: map, lambda y stl (remove_if). En realidad, el mismo código con std :: list o std :: vector funciona bien.map, lambda, remove_if

Mi ejemplo de prueba: Mensaje de

#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; 
} 

error:

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’ 

no entiendo lo que está mal aquí. Por lo tanto, me complace leer algunos consejos/indicaciones al respecto. Mi objetivo: utilizar un nuevo estilo lambda con std :: map y algoritmos, como remove_if.

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

+2

'remove_if' acepta un par de iteradores y devuelve un iterador. ¿Dónde crees que elimina los elementos ** de **? –

Respuesta

27

El problema es que std::map<K,V>::value_type es std::pair<const K, V>, también conocido como .first es const y no asignable. Lambdas no tiene nada que ver con el problema aquí.

std::remove_if "quita" elementos moviendo los elementos del contenedor alrededor, para que todo lo que no se ajuste al predicado esté en la parte delantera, antes del iterador devuelto. Todo después de ese iterador no está especificado. Lo hace con asignación simple, y como no puede asignar a una variable const, obtiene ese error.

El nombre remove puede ser un poco engañoso y en este caso, usted realmente quiere erase_if, pero por desgracia, eso no existe. Vas a tener que conformarse con la iteración en todos los artículos y borrarlos 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; 
} 

Esto es seguro porque se puede borrar nodos individuales en el árbol sin los otros iteradores siendo invalidados. Tenga en cuenta que no incrementé el iterador en el encabezado for del bucle, ya que eso saltaría un elemento en el caso de que borre un nodo.


† A estas alturas, debería haber dado cuenta de que esto podría causar estragos en el ordenamiento std::map 's, que es la razón por la cual la clave es const - por lo que no pueden influir en el orden de ninguna manera después de una el artículo ha sido insertado

+0

Gracias por la respuesta. Entonces, hay una forma elegante de eliminar el elemento de std :: map sin el código feo: 'void removeFromMap (FooMap & m, int id) { \t for (auto it = m.begin(), end = m. final (!), es = extremo; ++ es) \t \t { \t \t si (IT-> second._id == id) \t \t \t { \t \t \t m.Bórralo); \t \t \t break; \t \t} \t} } ' – Reddy

+0

@Reddy: No, no hay otra forma que yo sepa. Por cierto, si sus identificaciones no son únicas, solo borrará el primer elemento en el mapa. Si lo son, entonces ese ciclo está bien. – Xeo

+0

Sí, lo sé. – Reddy

3

Puede usar buscar y borrar para ver el mapa. No es tan conveniente como remove_if, pero podría ser lo mejor que tienes.

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); 
}