2009-11-23 9 views
13

Tengo un objeto que quiero viajar en un bucle continuo en un juego. Tengo una serie de coordenadas en un std::vector que quiero usar como waypoints.¿La manera más fácil de hacer un iterador cíclico (circulador)?

¿Hay alguna manera de hacer un std::vector<T>::iterator cíclico (también conocido como circulador)?

Lo mejor que se me ocurre es tener dos iteradores y cada vez que se agote el primer iterador le asigno el valor del segundo (que no se usaría para hacer otra cosa) pero ni siquiera estoy seguro de que funcionará: ¿el operador de asignación copiará lo que sea que el iterador esté usando para mantener el índice o simplemente se lo referenciará (y por lo tanto será inútil después de la segunda ronda)?

Quiero que el objeto recorra el waypoint para siempre (a menos que se destruya pero eso no sucede en ese método), pero el iterador solo se llamará una vez para cada fotograma y debe regresar para que pueda actualizar el otro objetos en el juego.

La solución debe funcionar en el compilador gcc y microsoft (si no es posible escribirlo en C++ estándar).

+0

He escrito un iterador como ese, así que es definitivamente posible =) El único giro que recuerdo es que el operador de comparación

+0

Por otro lado, ¿realmente necesita 'operator '? ¡Es un poco travieso ocultar el comportamiento cíclico! –

Respuesta

21

Ok, ahora el problema es más claro :-)

Tome un vistazo a impulso :: iterator_facade e impulsar :: adaptador de iterador. Que implementan la interfaz iterador completa y sus cycle_iterator sólo en cuanto a implementar algunos métodos como incremento(), decremento():

template<class IteratorBase> 
class cycle_iterator 
    : public boost::iterator_adaptor< 
      cycle_iterator,  // the derived class overriding iterator behavior 
      IteratorBase,  // the base class providing default behavior 
      boost::use_default, // iterator value type, will be IteratorBase::value_type 
      std::forward_iterator_tag, // iterator category 
      boost::use_default // iterator reference type 
     > 
{ 
    private: 
    IteratorBase m_itBegin; 
    IteratorBase m_itEnd; 

    public: 
    cycle_iterator(IteratorBase itBegin, IteratorBase itEnd) 
     : iterator_adaptor_(itBegin), m_itBegin(itBegin), m_itEnd(itEnd) 
    {} 

    void increment() { 
     /* Increment the base reference pointer. */ 
     ++base_reference(); 

     /* Check if past-the-end element is reached and bring back the base reference to the beginning. */ 
     if(base_reference() == m_itEnd) 
      base_reference() = m_itBegin; 
    } 

    // implement decrement() and advance() if necessary 
    }; 

Esto probablemente no compila pero deben empezar.

Editar:

boost::iterator_adaptor implementa la interfaz iterador completa en términos de algunas funciones. Proporciona implementaciones predeterminadas para increment(), decrement(), advance(), distance_to(), equal_to() y dereference() usando el iterador base pasado a la clase base iterator_adaptor.

Si todo lo que necesita es un iterador directo, solo se debe implementar el método increment() para envolverlo una vez que llegue al final del iterador. Un iterador cíclico puede ser bidireccional si implementa decrement() de manera similar. Si IteratorBase es en sí mismo un iterador de acceso aleatorio, el iterador de ciclo también puede ser de acceso aleatorio y el método advance y distance_to debe implementarse utilizando operaciones de módulo.

+0

+1: 'iterator_adaptor' es la forma más fácil de escribir iteradores, y los ejemplos son simplemente geniales (especialmente cómo escribir solo un adaptador y obtener las versiones const y non-const) –

+0

¿Hay alguna manera de hacer que los operadores binarios como '! =' para trabajar con argumentos del tipo 'cycle_iterator ' y 'IteratorBase'? Estoy tratando de comparar iterador 'cycle_iterator' y" usual "y necesito hacer una sobrecarga explícita en este caso. – Mikhail

+1

'increment()' necesita métodos de trabajo - Yo pensaría idealmente que incrementar el valor antes de 'end' debería darle el' begin'ning, no el 'end' en un iterador cíclico. – Yakk

-5

Derive su propia colección de std :: vector y proporcione su propia implementación de iterador que anule el incremento & operadores de decremento.

Hay muchos tutoriales en la web. Por ejemplo, echar un vistazo a this blog post

+3

¿deriva del vector? ¿Alguna vez has intentado eso? Estoy en desacuerdo. Agregue en su lugar! – xtofl

+0

NUNCA he visto una clase derivada de un contenedor estándar que fuera mejor para ella. Agregar. –

+1

Estoy de acuerdo con los comentarios. Respuesta publicada a toda prisa, arrepintiéndose en el ocio. Sin embargo, no puede eliminarlo porque ha sido aceptado como una respuesta. –

7

boost::iterator adaptor es el camino a seguir, tome mi palabra para ella;)

Dicho esto quiero señalar algunas trampas. No creo que pueda editar una respuesta existente, así que tengan paciencia conmigo.

Dado que su iterador base va a ser un vector, debe tener cuidado con las funciones de la interfaz central que necesita implementar.Si desea que su cycle_iterator para ser un iterador de acceso aleatorio que necesita todo lo siguiente:

increment() 
decrement() 
advance(n) 
distance_to(j) 

Ahora distance_to(j) es un concepto algo divertido para un cycle_iterator y su semántica que puede conseguir en todo tipo de problemas. Esto se puede evitar restringiendo la categoría del iterador del iterador adaptado a adelante o bidireccional. De esta manera:

template <class BaseIterator> 
class cycle_iterator 
    : public boost::iterator_adaptor< 
     cycle_iterator     // Derived 
     , BaseIterator     // Base 
     , boost::use_default    // Value 
     , boost::forward_traversal_tag // CategoryOrTraversal 
    > 
{ ... }; 

En este caso sólo es necesario para poner en práctica la subasta:

void increment() 
{ 
    if (++this->base_reference() == this->m_itEnd) 
    { 
    this->base_reference() = this->m_itBegin; 
    } 
} 

Para un bidireccional también necesita decremento:

void decrement() 
{ 
    if (this->base_reference() == this->m_itBegin) 
    { 
    this->base_reference() = this->m_itEnd; 
    } 
    --this->base_reference(); 
} 

Negación: no se ha ejecutado esto a través un compilador, entonces estoy listo para avergonzarme.

+0

Creo que, en general, 'distance_to (j)' puede devolver un número positivo o negativo que, cuando se agrega a un iterador, produce un iterador que apunta a 'j'. Puede decidir que desea devolver el número más pequeño que no sea negativo, o el número que tenga el valor absoluto más pequeño. En la mayoría de los casos, este último probablemente tenga más sentido o, de lo contrario, ¿existe un argumento fuerte (un contraejemplo)? –

Cuestiones relacionadas