2009-06-04 1 views
21

Рассмотрим:map.erase (map.end())?

#include <map> 

int main() 
{ 
    std::map< int, int > m; 
    m[ 0 ] = 0; 
    m[ 1 ] = 1; 

    m.erase(0); // ok 
    m.erase(2); // no-op 
    m.erase(m.find(2)); // boom! 
} 

(. Хорошо, так титульных переговоры abouting стирания конца() итератора, но найти вернет конец() для несуществующего ключа)

Почему удаление Беспоставочного - существующий ключ OK, но стирание end() взрывается. Я не видел никакого явного упоминания об этом в стандарте?

Я попробовал это на VS2005 (бросает исключение в отладочной конфигурации) и GCC 4.0.1 (100% CPU). Это зависит от реализации?

Спасибо.

ответ

30

Для erase(key), стандарт говорит, что все элементы с ключом значения удаляются. Разумеется, таких ценностей не может быть.

For erase(it) (где it является std::map::iterator), стандарт говорит, что элемент, на который указывает он удаляется - к сожалению, если это end() не указывает на действительный элемент и вы отключены в непредсказуемое поведение земель , как и было бы, если вы использовали end() для любой другой операции с картой. Подробности см. В разделе 23.1.2.

+3

Чтобы уточнить: существуют различные перегрузки erase(), а для версии итератора требуется действительный элемент. – rlbond

+12

erase (it) эквивалентно стиранию (it, ++ iterator (it)), что помогает мне видеть, что с его помощью() оно недействительно = map.end(). Вам понадобится другой итератор после .end(). –

+0

Может ли кто-нибудь предоставить ссылку на стандарт? –

18

end() не является interator в карте. Это действительно «один конец» карты.

«итератор» версия хочет итератор к чему-то в карте.
«key» версия erase выполняет поиск и защищает себя от того, что ключ не найден, версия итератора предполагает, что вы не пытаетесь сломать материал.

+0

«Предполагается, что вы не пытаетесь сломать материал» ... Я понимаю, я надеялся, что стереть (он) сделает простую проверку, что это! = End() –

+0

Не безосновательная мысль, это просто то, что многие части контейнеров STL не хотят накладных расходов на такие проверки. В случаях, подобных стиранию, итератор, вероятно, использовался для чего-то еще, прежде чем вы захотите стереть запись, поэтому они не учитывают чек для завершения - потому что вы, вероятно, уже сделали это. В ключевом случае более вероятно, что вызывающий абонент не знает, находится ли ключ на карте или нет, поэтому они выполняют проверку. –

+0

Дополнение к предыдущему комментарию, просто для лучшего понимания карты: это дерево двоичного поиска (стандарт требует его упорядочивания, чаще всего реализуемого как красно-черное дерево), и для стирания ключа сначала нужно его найти во всяком случае, существует ли это или нет, - так что вы болтаете по дереву (делаете стирание (ключ) операцией O (log n)) в любом случае, и принятие несуществующего ключа не подразумевает никакой дополнительной работы (как и проверка in erase (it)), поэтому логично принять его как вход. – Aconcagua

1

Вот краткий пример того, как я использую карту STL с итераторами при удалении. Я также делаю то же самое при выполнении вставки. Лично мне нравится использовать typedef для конкретного определения карты, но выбор за вами.


typedef std::map... MapType; 

MapType the_map; 

MapType::iterator it = the_map.find ("key"); 
if (it != the_map.end()) { 
    // Do something productive. 
    the_map.erase (it); 
} 

MapType::iterator it = the_map.find ("new_key"); 

// Does not exist. 
if (it == the_map.end()) { 
    the_map.insert (std::make_pair ("new_key", 10)); 
} 

Надеюсь, что это поможет!

3

Вместо примера, приведенного в предыдущем посте ...

MapType::iterator it = the_map.find ("new_key"); 

// Does not exist. 
if (it == the_map.end()) { 
    the_map.insert (std::make_pair ("new_key", 10)); 
} 

, который делает два дерева обходы, использование ...

pair<MapType::iterator, bool> rc = the_map.insert(make_pair("new_key", 0)); 
if (rc.second) 
    rc.first.second = 10; 

Таким образом, вы делаете одно дерево обхода и у вас есть итератор готов к работе для других вещей.

Смежные вопросы