Tengo una clase llamada Action, que es esencialmente una envoltura alrededor de una deque de Move objetos.

Porque tengo que atravesar el deque de Moves hacia adelante y hacia atrás, tengo un avance iterador y un reverse_iterator como variables miembro de la clase. La razón de esto es porque necesito saber cuando he ido de un pasado de la «final» de la deque, tanto cuando voy hacia delante o hacia atrás.

La clase se parece a esto:

class Action
{
public:
    SetMoves(std::deque<Move> & dmoves) { _moves = dmoves; }
    void Advance();
    bool Finished() 
    {
        if( bForward )
            return (currentfwd==_moves.end());
        else
            return (currentbck==_moves.rend());
    }
private:
    std::deque<Move> _moves;
    std::deque<Move>::const_iterator currentfwd;
    std::deque<Move>::const_reverse_iterator currentbck;
    bool bForward;
};

La Advance función es como sigue:

void Action::Advance
{
    if( bForward)
        currentfwd++;
    else
        currentbck++;
}

Mi problema es que quiero ser capaz de recuperar un iterador a la actual Move objeto, sin necesidad de la consulta de si voy hacia delante o hacia atrás. Esto significa que una función devuelva un tipo de iterador, pero tengo dos tipos.

Debo olvidar devuelve un iterador, y devolver una referencia constante a un Move objeto en su lugar?

mejores deseos,

BeeBand

  • La respuesta a su pregunta «¿puedo obtener un reverse_iterator de un delantero iterador» es y aquí
InformationsquelleAutor BeeBand | 2010-01-10

5 Comentarios

  1. 27

    Este es exactamente el tipo de problema que se le solicite el diseño de STL para empezar. Hay razones reales para:

    1. No almacenar los iteradores junto con los contenedores de
    2. el Uso de algoritmos que aceptar arbitraria iteradores
    3. los algoritmos que evaluar todo un rango en lugar de un solo elemento a la vez

    Sospecho que lo que estamos viendo ahora es más o menos la punta del iceberg de los problemas reales. Mi consejo sería dar un paso atrás, y en lugar de preguntar acerca de cómo lidiar con los detalles del diseño, como se encuentra actualmente, pedir un poco de la cuestión más general acerca de lo que estamos tratando de lograr, y la mejor forma de lograr que el resultado final.

    Para aquellos que se preocupan principalmente acerca de la pregunta en el título, la respuesta es altamente calificados «sí». En particular, un reverse_iterator tiene un base() miembro para hacer eso. Las calificaciones son un poco problemático, aunque.

    El demostrar el problema, considere la posibilidad de un código como este:

    #include <iostream>
    #include <vector>
    #include <iterator>
    
    int main() { 
        int i[] = { 1, 2, 3, 4};
        std::vector<int> numbers(i, i+4);
    
        std::cout << *numbers.rbegin() << "\n";
        std::cout << *numbers.rbegin().base() << "\n";
        std::cout << *(numbers.rbegin()+1).base() << "\n";
    
        std::cout << *numbers.rend() << "\n";
        std::cout << *numbers.rend().base() << "\n";
        std::cout << *(numbers.rend()+1).base() << "\n";
    }

    Ejecutando en este momento particular de mi máquina en particular produce la siguiente salida:

    4
    0
    4
    -1879048016
    1
    -1879048016

    Resumen: con rbegin() nos debe agregar uno antes de convertir a un iterador para obtener un iterador que es válido, pero con rend() debemos no agregar uno antes de la conversión para obtener una válida iterador.

    Mientras usted está usando X.rbegin() y X.rend() como los parámetros de un algoritmo genérico, que está bien … pero la experiencia indica que la conversión a adelante iteradores a menudo conduce a problemas.

    Al final, sin embargo, para el cuerpo de la pregunta (en comparación con el título), la respuesta es bastante parecido a como se indica más arriba: el problema se deriva de intentar crear un objeto que combina la colección con un par de iteradores en esa colección. Solucionar ese problema, y todo el negocio con el avance y retroceso de los iteradores se convierte en irrelevante.

    • Me gusta tu respuesta. Usted podría estar en lo correcto. Yo soy relativamente nuevo en C++ y de la STL. Y lo que constituye la buena C++ diseño es algo con lo que estoy luchando para aprender.
    • Aunque esta respuesta ayuda a BeeBand que no responde a la pregunta original para la posteridad. Esta respuesta debería ser un comentario al post original. Me gustaría preguntar a BeeBand a considerar la posibilidad de cambiar la garrapata a Mike Seymour respuesta.
    • Si se limita «a la pregunta» a lo que está en el título, tienes razón. Si usted lee la totalidad de la pregunta en sí misma, sin embargo, yo diría que mucho menos (en el mejor).
    • Cuatro años en el camino, yo diría que la gente encuentra esta cuestión debido a su título más que a causa de su contenido.
    • Usted señor son correctos. 🙂
  2. 65

    Inversa iteradores de tener a un miembro base() que devuelve un valor correspondiente adelante iterador. Ten en cuenta que este no un iterador que se refiere al mismo objeto – que en realidad se refiere al objeto siguiente en la secuencia. Esto es para que rbegin() corresponde con end() y rend() corresponde con begin().

    Así que si usted desea devolver un iterador, entonces usted haría algo como

    std::deque<Move>::const_iterator Current() const
    {
        if (forward)
            return currentfwd;
        else
            return (currentbck+1).base();
    }

    Yo preferiría volver a una referencia, aunque, y encapsular todos los iteración de los detalles dentro de la clase.

    • (currentbck+1).base() borks cuando currentbck es un iterador. Conversión entre los dos es un mundo de errores esperando a suceder.
  3. 6

    Desde std::deque es un de acceso aleatorio contenedor (igual que std::vector) usted es mucho mejor el uso de un único índice entero en el deque para ambos recorridos.

    • Gracias – he estado usando deque todo el resto de la aplicación por esta misma razón. No sé por qué tuve una visión de túnel sobre los iteradores 🙂
    • Es por eso que siempre se necesita un segundo par de ojos 🙂
    • Pero tenga cuidado: es muy difícil de probar un entero sin signo para saber si ha llegado a sub zero valor 😉
    • Buen punto. Entero con signo, a continuación, supongo.
    • Usted podría utilizar un (hacia adelante) iterador, y tenga cuidado de no incrementar, si es igual a end () o disminuir si igual a end(). Y de cualquier forma, watch para las operaciones que invalidan los iteradores, ya que también podría invalidar un índice (ya sea porque ya no apunta al elemento que piensa, o porque quita algo cuando el índice se refiere a la final de la deque).
    • A qué te refieres no reduzcas si igual a begin()? El problema con eso es que si Antelación() no van a la equivalente funcional de rend(), una función como GetCurrentMove() devolverá begin (), cuando en realidad todos los movimientos han sido procesados.
    • Sí, eso es lo que quiero decir, lo otro era una tontería :-). Usted tendría que usar una bandera para indicar que «fuera el principio», y el caso especial en todas partes. El problema es que no hay en iterador tipo que puede ir de los dos extremos de un intervalo, por lo que GetCurrentMove() no encaja bien con lo que estás haciendo. E incluso un fin de iterador no apuntan a nada. Por lo que deberás tener especial-caso de que ambos extremos de todos modos. Sólo una sugerencia, no sé si realmente va a funcionar mejor que el uso de un índice.

  4. 1

    A mí me parece que en realidad tiene dos comportamientos diferentes en la misma clase.

    En particular, parece que sólo se puede recorrer su colección en un solo pedido, de lo contrario, si usted fuera a comenzar el recorrido y, a continuación, cambiar el bforward argumento se podría acabar con una situación extraña.

    Personalmente, estoy a favor de exponer tanto los iteradores (es decir, hacia adelante begin, end, rbegin and rend).

    También puede devolver un simple objeto Iterador:

    template <class T>
    class Iterator
    {
    public:
      typedef typename T::reference_type reference_type;
    
      Iterator(T it, T end) : m_it(it), m_end(end) {}
    
      operator bool() const { return m_it != m_end; }
    
      reference_type operator*() const { return *m_it; }
    
      Iterator& operator++() { ++m_it; return *this; }
    
    private:
      T m_it;
      T m_end;
    };
    
    template <class T>
    Iterator<T> make_iterator(T it, T end) { return Iterator<T>(it,end); }

    Entonces, usted puede devolver este simple objeto:

    class Action
    {
    public:
      Action(std::deque<Move> const& d): m_deque(d) {} //const& please
    
      typedef Iterator< std::deque<Move>::iterator > forward_iterator_type;
      typedef Iterator< std::deque<Move>::reverse_iterator > backward_iterator_type;
    
      forward_iterator_type forward_iterator()
      {
        return make_iterator(m_deque.begin(), m_deque.end());
      }
    
      backward_iterator_type backward_iterator()
      {
        return make_iterator(m_deque.rbegin(), m_deque.rend());
      }
    
    
    private:
      std::deque<Move> m_deque;
    };

    O si desea elegir dinámicamente entre adelante y atrás recorrido, usted podría hacer un Iterador puro interfaz virtual y contar hacia adelante y hacia atrás del recorrido.

    Pero realmente, realmente no veo el punto de almacenamiento hacia adelante y hacia atrás iterador si parece que se va a utilizar sólo uno :/

    • Me gusta esta solución, y que podría ser un buen ejercicio de aprendizaje para mí. La razón para almacenar tanto los iteradores es porque la «GetCurrentMove() se llama desde un lugar diferente en la app para Avanzar(). Así que necesito un lugar conveniente para almacenar el «cambio».
    • Que es normalmente la función de iterador, aunque su aplicación como 2 diferentes objetos en C++, aunque guarda algún lugar e imita aritmética de punteros, es bastante molesto, creo. El Iterador de arriba está inspirado en Python > en un solo objeto de mantener su posición actual y el punto final. En su aplicación, no es necesario pasar la plena Action clase, usted sólo tiene que pasar el Iterator (o si se va a hacer referencia directamente a la deque los iteradores, la posición actual y el final). De esta manera, promover la disociación 🙂
  5. 0

    Tal vez debería reconsiderar su elección de contenedores.

    Por lo general, usted no necesita usar la marcha atrás iteradores para ir hacia atrás,

    currentfwd--

    va a ir para atrás, todos, aunque es posible que no funciona (que supongo que probé) con la cola.

    Lo que realmente debería hacer es modelo de su clase aquí como un decorador de cola y poner en práctica su propia Acción de los iteradores. Eso sería lo que yo haría de todos modos.

    • Gracias Charles. El problema con ir hacia atrás es en el Acabado() función – necesito knwo cuando yo soy uno antes de que el primer elemento ( es decir, han pasado el «rend()» ).

Dejar respuesta

Please enter your comment!
Please enter your name here