Стереть определенные элементы в 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>>;