Как стереть reverse_iterator из структуры данных stl? - PullRequest
14 голосов
/ 01 января 2009

По какой-то причине следующий код не работает. Вы не можете просто удалить 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;
    }

}

Как правильно поступить, как описано выше? Если вы используете reverse_iterator, который соответствует тому, что вы хотите стереть, как вы его удаляете?

Обратите внимание, к сожалению, стереть не удастся Он хочет настоящего.

Ответы [ 4 ]

18 голосов
/ 01 января 2009

Очевидно, что решение, которое возвращает base () - это 1 off. Следующее удостоверение верно для reverse_iterator:

&*(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;
        }
    }   

}

Обратите внимание, ассоциативные контейнеры не возвращают итератор от стирания. Так что это решение не будет работать для карты, мультикарты и т. Д.

3 голосов
/ 04 мая 2009

Когда вы выполняете итерацию с помощью обратного итератора и хотите использовать 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);
0 голосов
/ 08 января 2014

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
0 голосов
/ 01 января 2009

Вызовите 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 (или подобную ему функцию).

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...