Предположим, у меня есть hash_map и такой код, как
// i is an iterator
i = hash_map.erase(i)
Но STL GCC возвращает не итератор в стирании, а пустоту. Теперь это код вроде
hash_map.erase(i++)
безопасно (т.е.не делает недействительным итератор и не делает других неожиданных или неприятных вещей)? Обратите внимание, что это hash_map.
Да, это безопасно, потому что значение i
будет установлено на следующее значение до того, как текущее значение будет удалено.
Согласно Документация SGI о хешированных контейнерах недействительность не происходит ни для не удаленных элементов, ни даже для изменения размера (нет слов о том, вызывают ли вставки изменение размера, поэтому будьте осторожны, я допускаю это как возможность), но в последнем случае порядок итераций будет изменен. Но здесь это не применимо, если вы не изо всех сил измените размер контейнера во время обхода или чего-то еще. :-)
Это на той странице, на которую есть ссылка в моем сообщении. Я предполагаю, что для этого сайта, если специально не указано иное, операции не приводят к аннулированию. См. sgi.com/tech/stl/Vector.html (например), где упоминаются все случаи признания недействительными.
(Я выбрал вектор специально, потому что этот тип данных имеет (по-видимому) больше случаев недействительности, чем любой другой.)
Ненавижу лить дождь на параде, но я не думаю, что то, что вы предлагаете, безопасно.
i ++ - это оператор постинкремента, что означает, что i увеличивается на единицу после вызова стирания. Но стирание делает недействительными все итераторы, указывающие на удаляемый элемент. Таким образом, к тому времени, когда i увеличивается, он больше не действителен.
Если вам повезет, он может случайно сработать правильно, пока однажды он не перестанет работать.
Насколько мне известно, обойти это невозможно, но что-то вроде:
// tmp and i are both iterators
tmp = i;
++i;
hash_map.erase(tmp);
Согласно стандарту it ++ и it-- (где это итератор) эквивалентны созданию равной ему temp, уменьшению итератора и возвращению temp.
Да, но компилятор может вызывать (i ++) до или после вызова стирания, поэтому, если компилятор решит сделать (i ++) после стирания, то i недействителен.
Родилэнд, у вас есть какие-нибудь ссылки на это утверждение? Я никогда об этом не слышал.
Я прочитал дальше и, похоже, ошибся - компилятор вынужден оценивать (i ++) перед вызовом erase, так что это безопасно. Тем не менее, мне это все еще не нравится.
Я согласен с тем, что это не очень безопасный элемент, но в этом случае определяется использование оператора приращения. Подробнее на эту же тему: stackoverflow.com/questions/436013/…
Вы можете инкапсулировать стирание, чтобы обеспечить одинаковый интерфейс для всех используемых контейнеров:
namespace detail {
template<typename Container, typename R>
struct SelectErase {
// by default, assume the next iterator is returned
template<typename Iterator>
Iterator erase(Container& c, Iterator where) {
return c.erase(where);
}
};
// specialize on return type void
template<typename Container>
struct SelectErase<Container, void> {
template<typename Iterator>
Iterator erase(Container& c, Iterator where) {
Iterator next (where);
++next;
c.erase(where);
return next;
}
};
template<typename I, typename Container, typename R>
SelectErase<Container,R> select_erase(R (Container::*)(I)) {
return SelectErase<Container,R>();
}
} // namespace detail
template<typename Container, typename Iterator>
Iterator erase(Container& container, Iterator where) {
return detail::select_erase<Iterator>(&Container::erase).erase(container, where);
}
Для этого требуется либо:
Можете указать, где вы это читаете? Это верно для ассоциативного контейнера, как определено в ISO C++, но я никогда не читал ничего подобного в документации SGI.