карта, лямбда, удалить если
Итак, у меня проблема с алгоритмом std::map, lambda и stl(remove_if). На самом деле, тот же код с std::list или std::vector работает хорошо.
Мой тестовый пример:
#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;
}
Сообщение об ошибке:
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’
Я не понимаю, что здесь не так. Поэтому я с удовольствием прочитаю несколько советов / указаний по этому поводу. Моя цель-использовать новый лямбда-стиль с std:: map и алгоритмами, такими как remove_if.
G++ 4.6, - std=c++0x.
2 ответа:
Проблема в том, что
std::map<K,V>::value_type
являетсяstd::pair<const K, V>
, он же.first
являетсяconst
и не присваивается. Лямбды не имеют никакого отношения к этой проблеме.
std::remove_if
"удаляет" элементы, перемещая элементы контейнера вокруг, так что все, что не соответствует предикату, находится впереди, перед возвращаемым итератором. Все, что после этого итератора не определено. Он делает это с помощью простого присваивания, и поскольку вы не можете присвоить переменнойconst
, Вы получаете это ошибка.†Название
remove
может быть немного обманчивым, и в этом случае вы действительно хотитеerase_if
, но, увы, этого не существует. Вам придется довольствоваться повторением всех элементов и стиранием их вручную с помощьюmap.erase(iterator)
:Это безопасно, потому что вы можете стереть отдельные узлы в дереве без того, чтобы другие итераторы стали недействительными. Обратите внимание, что я не увеличивал итератор в самом заголовке цикла for, так как это пропустило бы элемент в случае где вы стираете узел.for(auto it = map.begin(), ite = map.end(); it != ite;) { if(it->second._id == remove_id) it = map.erase(it); else ++it; }
К настоящему моменту вы должны были заметить, что это приведет к хаосу в порядкеstd::map
, поэтому ключconst
- так что вы не можете повлиять на порядок каким-либо образом после того, как элемент был вставлен.
Вы можете использовать find and erase для карты. Это не так удобно, как remove_if, но это может быть лучшее, что у вас есть.
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); }