Как стереть обратный_тератор из stl-структуры данных?
По какой-то причине следующий код не работает. Вы не можете просто стереть reverse_iterator с помощью метода base().
#include <set>
#include <iostream>
int main()
{
std::set<int> setOfInts;
setOfInts.insert(1);
setOfInts.insert(2);
setOfInts.insert(3);
std::set<int>::reverse_iterator rev_iter = setOfInts.rbegin();
std::set<int>::reverse_iterator nextRevIter = setOfInts.rbegin();
++nextIter;
while ( rev_iter != setOfInts.rend())
{
// Find 3 and try to erase
if (*rev_iter == 3)
{
// SEGFAULT HERE
setOfInts.erase( rev_iter.base());
}
rev_iter = nextRevIter;
++nextRevIter;
}
}
Как правильно все это делать? Учитывая обратный_источник, который соответствует чему-то, что вы хотите стереть, как его стереть?
Примечание. Устранение не приведет к обратному обращению. Он хочет настоящую вещь.
Ответы
Ответ 1
По-видимому, решение - это то, что base() возвращает 1. Для обратного_тератора выполняется следующее тождество:
&*(reverse_iterator(i)) == &*(i - 1)
Или, другими словами, reverse_iterator всегда один проход обычного итератора, который является базой. Не знаю, почему.
В GCC
Просто измените
// SEGFAULT HERE
setOfInts.erase( rev_iter.base());
к
// WORKS!
setOfInts.erase( --rev_iter.base());
Мне определенно любопытно, но почему тождество выше имеет смысл.
В Visual Studio
Возвращаясь к работе и пробуя это в visual studio, я вижу, что вышеупомянутое решение не совсем работает. При стирании "nextIter" становится недействительным. Вместо этого вам нужно сэкономить временное от стирания, чтобы получить следующий итератор вместо того, чтобы хранить следующий элемент, как указано выше.
set<int>::iterator tempIter = setOfInts.erase(--rev_iter.base());
rev_iter = setOfInts.erase(tempIter);
Итак, окончательное решение
int main()
{
using namespace std;
set<int> setOfInts;
setOfInts.insert(1);
setOfInts.insert(2);
setOfInts.insert(3);
set<int>::reverse_iterator rev_iter = setOfInts.rbegin();
while ( rev_iter != setOfInts.rend())
{
// Find 3 and try to erase
if (*rev_iter == 3)
{
cout << "Erasing : " << *rev_iter;
set<int>::iterator tempIter = setOfInts.erase( --rev_iter.base());
rev_iter = set<int>::reverse_iterator(tempIter);
}
else
{
++rev_iter;
}
}
}
Примечание. Ассоциативные контейнеры не возвращают итератор из стирания. Таким образом, это решение не будет работать для карты, multimap и т.д.
Ответ 2
Когда вы выполняете итерацию с помощью обратного итератора и хотите использовать base() для изменения своего контейнера, всегда имейте в виду, что reverse_iterator всегда основан на следующем итераторе из исходного порядка. Это немного неинтуитивно, но на самом деле делает код проще:
#include <set>
int main()
{
std::set<int> setOfInts;
setOfInts.insert(1);
setOfInts.insert(2);
setOfInts.insert(3);
typedef std::set<int>::reverse_iterator RevIter;
RevIter rev_iter = setOfInts.rbegin();
while (rev_iter != setOfInts.rend())
{
// Find 3 and try to erase
if (*rev_iter == 3)
setOfInts.erase(--rev_iter.base());
++rev_iter;
}
}
В этом примере нет необходимости хранить "следующий" итератор, поскольку базовый итератор не является недействительным! (Нам это нужно при работе с обычными итераторами.)
Поведение обратных итераторов создает странные трудности при работе с одним элементом, но в фактах упрощает диапазоны:
riValue = find(riEnd.base(), riBegin.base(), value);
использует точно такие же объекты (в обратном порядке) как
iValue = find(riBegin, riEnd, value);
Ответ 3
1 из map:: erase, мы знаем, что он принимает только iterator
;
2 из reverse_iterator:: base, мы знаем &*(reverse_iterator ( i ) ) == &*( i – 1 ).
Поэтому вы можете стереть (- r_v.base()), чтобы стереть элемент, обозначенный "r_v" (и "current-1" ):
r_v+1 r_v r_v-1
current-2 current-1 current
Ответ 4
Вызовите erase
с самим итератором (не нужно использовать base
).
#include <set>
#include <iostream>
int main()
{
std::set<int> setOfInts;
setOfInts.insert(1);
setOfInts.insert(2);
setOfInts.insert(3);
std::set<int>::reverse_iterator rev_iter = setOfInts.rbegin();
while (rev_iter != setOfInts.rend())
{
// Find 3 and try to erase
if (*rev_iter == 3)
{
rev_iter = setOfInts.erase(rev_iter);
}
else
{
++rev_iter;
}
}
}
Кроме того, вам не нужен отдельный "следующий" итератор (см. выше изменения). Еще лучший способ сделать это - использовать std::remove_if
(или такую функцию).