Pregunta

Por alguna razón, el siguiente código falla. No puedes simplemente borrar un reverse_iterator usando su método 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;
    }

}

¿Cómo se hace uno correctamente haciendo lo anterior? Dado un reverse_iterator que corresponde a algo que desea borrar, ¿cómo lo borra?

Nota, el borrado no tomará Reverse_iterators por desgracia. Quiere lo real.

¿Fue útil?

Solución

Al parecer, la solución es lo que devuelve base () es 1 apagado. La siguiente identidad se mantiene para un reverse_iterator:

&*(reverse_iterator(i)) == &*(i - 1) 

O, en otras palabras, el reverse_iterator es siempre una pasada del iterador regular del que es la base. No estoy seguro de por qué.

En GCC

Simplemente cambia

        // SEGFAULT HERE
        setOfInts.erase( rev_iter.base());

a

        // WORKS!
        setOfInts.erase( --rev_iter.base());

Definitivamente tengo curiosidad por saber por qué la identidad anterior tiene sentido.

En Visual Studio

Volviendo al trabajo y probando esto en Visual Studio, veo que la solución anterior no funciona del todo. El " nextIter " se invalida en el borrado. En su lugar, debe guardar el temporal del borrado para obtener el siguiente iterador en lugar de mantener un nextIter como el anterior.

  set<int>::iterator tempIter = setOfInts.erase(--rev_iter.base());
  rev_iter = setOfInts.erase(tempIter);

Así que la solución final es

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;
        }
    }   

}

Tenga en cuenta que los contenedores asociativos no devuelven un iterador de borrado. Entonces, esta solución no funcionaría para mapas, multimapas, etc.

Otros consejos

Cuando itera con un iterador inverso y desea usar base () para modificar su contenedor, siempre tenga en cuenta que un iterador inverso siempre se basa en el siguiente iterador del orden original. Es un poco poco intuitivo, pero en realidad hace que el código sea más simple:

#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;
    }
}

En este ejemplo, no es necesario mantener un " siguiente " iterador ya que el iterador base no se invalida! (Lo necesitamos cuando se trata de iteradores normales).

El comportamiento de los iteradores inversos crea extrañas dificultades off-by-one cuando se trata con un solo elemento, pero en los hechos simplifica los rangos:

riValue = find(riEnd.base(), riBegin.base(), value);

está utilizando exactamente los mismos objetos (en orden inverso) que

iValue = find(riBegin, riEnd, value);

1 de map :: erase , sabemos que solo se necesita iterator ;

2 de reverse_iterator :: base , sabemos & amp; * (reverse_iterator (i)) == & amp; * (i - 1).

Por lo tanto, puedes borrar (- r_v.base ()) para borrar el elemento apuntado por " r_v " (y " current-1 "):

            r_v+1            r_v          r_v-1
           current-2      current-1      current

Llame a erase con el propio iterador (no necesita utilizar 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;
        }
    }
}

Además, no necesita ese '' siguiente '' separado iterador (ver cambios anteriores). Una forma aún mejor de hacerlo es usar std :: remove_if (o una función como esta).

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top