Стереть определенные элементы в std:: map
Я хочу стереть некоторые элементы в моей std:: map.
Я написал метод erase + remove_if, который я всегда делаю с другими контейнерами последовательности.
Но он не был скомпилирован с map. Почему?
И как я могу выполнить эту работу?
std::map<int, int> m;
bool foo(const std::pair<int, int>& p)
{
return p.second > 15;
}
int _tmain(int argc, _TCHAR* argv[])
{
m.insert(make_pair(0, 0));
m.insert(make_pair(1, 10));
m.insert(make_pair(2, 20));
m.insert(make_pair(3, 30));
m.erase(
remove_if(m.begin(), m.end(), foo),
m.end()); // compile error
return 0;
}
6 ответов
напишите это так для карты, так как remove_if
не работает map
итераторы (он просто ставит оскорбительные элементы в конце, и map
итераторы не позволяют этого):
template <typename Map, typename F>
void map_erase_if(Map& m, F pred)
{
typename Map::iterator i = m.begin();
while ((i = std::find_if(i, m.end(), pred)) != m.end())
m.erase(i++);
}
или если вы любите ОДН-вкладыши:
template <typename Map, typename F>
void map_erase_if(Map& m, F pred)
{
for (typename Map::iterator i = m.begin();
(i = std::find_if(i, m.end(), pred)) != m.end();
m.erase(i++));
}
, потому что std::map
не является " контейнером последовательности" :)
remove_if
попытается поместить бесполезные элементы в конец карты, но это приведет к нарушению неявной структуры данных (в большинстве случаев красно-черное дерево) карты. Неявная структура данных определяет место каждого элемента на карте, и именно поэтому remove_if
не допускается std::map
.
вы должны удалить элементы из std::map
один за другим (или давая некоторый интервал) в цикле.
что вот так:
it = m.begin();
while ((it = std::find_if(it, m.end(), pred)) != m.end())
m.erase(it++);
" С другими контейнерами последовательности " ваша ошибка -map
это ассоциативные контейнер! В ассоциативных контейнерах элементы определяются их ключом (в отличие от порядка их вставки в контейнеры последовательности), и вы стираете элементы под ключ:
m.erase(12);
стирание по значению ключа имеет ту же сложность, что и поиск (например, O (log n) для карты, O(1) для неупорядоченной карты и т. д.). Кроме того, вы можете стереть итератором в постоянное время. Удаление итератора недействителен этот итератор, но нет других (опять же, в отличие от контейнеров последовательности), поэтому, если вы хотите перебирать карту, типичная идиома такова:
for (auto it = m.cbegin(); it != m.cend(); ) // no "++"!
{
if (it->second > 15) // your own condition goes here
{
m.erase(it++);
}
else
{
++it;
}
}
эта идиома работает только для последовательности, такой как контейнеры - записи в карте (ассоциативные) не могут быть переупорядочены (ключ не меняется-так как вы можете ожидать движение запись в другую позицию-например, конец). Правильный способ сделать это-найти запись и удалить ее - т. е. it = map.find(); map.erase(it++)
попробуйте что-то вроде этого
#include <iostream>
#include <map>
#include <algorithm>
class foo
{
public:
enum CompType { GREATER=1, LESS=-1 };
foo(int nVal=15, enum CompType ctype=GREATER)
: m_nVal(nVal)
, m_bGreater(ctype==GREATER)
{
}
bool operator()(std::pair<int, int> p)
{
if (m_bGreater)
return p.second > m_nVal;
else
return p.second < m_nVal;
}
private:
int m_nVal;
bool m_bGreater;
};
void MapRemove(std::map<int, int> &m, foo &pred)
{
auto itr = std::find_if(m.begin(), m.end(), pred);
while (itr != m.end())
itr = std::find_if(m.erase(itr), m.end(), pred);
}
int main(int argc, char *argv[])
{
std::map<int, int> m;
m.insert(std::make_pair(0, 0));
m.insert(std::make_pair(1, 10));
m.insert(std::make_pair(2, 20));
m.insert(std::make_pair(3, 30));
MapRemove(m, foo());
for (auto itr=m.begin(); itr!=m.end(); ++itr)
std::cout << "(" << itr->first << ", "
<< itr->second << ")" << '\n';
return 0;
}
но он не был скомпилирован с map. Почему?
при использовании remove_if
тип разыменование итератора должны соответствовать требованиям CopyAssignable. То есть должна быть возможность присвоить одно значение другому.
на std::map<int, int>
значение std::pair<const int, int>
который представляет пары ключевых значений карты и который не является CopyAssignable. Причина этого const int
ибо ключ-это то, как карта работает внутренне, как уже указали другие люди.
кстати вы получите те же ошибки компиляции для контейнера последовательности такой:
std::vector<std::pair<const int, int>>;