Карта, лямбда, remove_if
Итак, у меня проблема с std:: map, lambda и stl algorithm (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 = С++ 0x.
Ответы
Ответ 1
Проблема заключается в том, что std::map<K,V>::value_type
есть std::pair<const K, V>
, aka .first
is const
и не может быть назначен. Лэмбда не имеет ничего общего с проблемой здесь.
std::remove_if
"удаляет" элементы, перемещая элементы контейнера вокруг, так что все, что не соответствует предикату, находится спереди, перед возвратом итератор. Все после этого итератора не указано. Он делает это с простым назначением, и поскольку вы не можете назначить переменную const
, вы получите эту ошибку. †
Имя remove
может немного ввести в заблуждение, и в этом случае вы действительно хотите erase_if
, но, увы, этого не существует. Вам придется обойтись без повторения всех элементов и стирать их вручную с помощью 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;
}
Это безопасно, потому что вы можете стереть отдельные узлы в дереве без того, чтобы другие итераторы становились недействительными. Обратите внимание, что я не увеличил итератор в заголовке цикла for, так как это пропустит элемент в случае, когда вы удалите node.
† Теперь вы должны заметить, что это приведет к хаосу в заказе std::map
, поэтому ключ const
- так что вы не можете повлиять на порядок в любом случае после того, был вставлен.
Ответ 2
Вы можете использовать поиск и стирание для карты. Это не так удобно, как 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);
}