2012-07-11 13 views
9

Considere la siguiente secuencia:¿Cómo puedo escribir un contenedor de iteradores que combine grupos de valores secuenciales del iterador subyacente?

1, 2, 3, 4, 5, 6, 7, 8, 9, 10 

tengo iteradores de entrada para esa secuencia. Quiero envolver los iteradores de iteradores que producen la secuencia siguiente en su lugar:

(1,2), (3,4), (5,6), (7,8), (9,10) 

Si no está claro, esta secuencia es una secuencia de pares consecutivos de elementos consecutivos de la original. Mientras que el original tiene 10 elementos, este tiene 5: cada uno se obtiene de dos de la secuencia original.

estoy usando Boost iterator_facade para implementar esto, y tengo este intento equivocado en esto:

template <typename Iterator> 
    struct pairing_iterator 
    : boost::iterator_facade< 
     pairing_iterator<Iterator>, 
     std::array<typename std::iterator_traits<Iterator>::value_type, 2>, 
     std::input_iterator_category 
     // I should probably customize reference too, but it's not relevant 
    > { 
     pairing_iterator(Iterator it) : it(it) { 
      increment(); // A 
     } 
     pairing_iterator::value_type dereference() const { 
      return pair; 
     } 
     bool equal(pairing_iterator const& that) const { 
      return it == that.it; // B 
     } 
     void increment() { 
      pair = { { *it++, *it++ } }; 
     } 
     Iterator it; 
     pairing_iterator::value_type pair; 
    }; 

Uno de los problemas que estoy enfrentando es en la línea marcada con A: cuando el iterador es aprobada en un iterador final, esto dará como resultado incrementarlo, lo que no puedo hacer.

Otro está en la línea marcada con B: estoy manteniendo el iterador subyacente siempre por delante del par "actual", por lo que si el iterador está en el último par, el iterador subyacente será un iterador final, y por lo tanto compare verdadero contra un final pairing_iterator.

Si el iterador subyacente era iterador directo, simplemente podría leer el par cada vez que desreferencia, y simplemente avanzar dos veces al incrementar. Pero con iteradores de entrada solo puedo leer una vez.

¿Estoy reinventando una rueda que ya existe en alguna parte? No encontré nada como esto en Boost, lo que me sorprende un poco. Pero me encantaría encontrar una solución ya hecha.

Si esta rueda ya no existe, ¿cómo puedo hacer que ruede realmente?

+0

En mi opinión, esto es similar a un iterador de filtrado. Yo diría que es mejor que el iterador contenga el iterador final, para manejar el caso en el que la secuencia contiene inesperadamente un número impar de elementos. Esto también resolvería sus dos problemas (A y B), ya que sabe si es un iterador final, y puede separar la realización del par de proxy del incremento del iterador subyacente. (No estoy tan familiarizado con la fachada del iterador de Boost, así que no estoy del todo seguro de cómo traducir esto a Boosteese). –

+0

'Desreferencia de enteros()'? –

+0

@David oops, eso fue un artefacto de mí 'comprar detalles innecesarios lejos del ejemplo :) –

Respuesta

1

tengo dos sugerencias ya derribado en el chat, uno con un extraño, pero la limitación relativamente seguro, y uno con una solución fea de la limitación:

primera idea es muy simple, pero requiere exactamente un desreferenciar antes cada avance

template <typename Iterator> 
struct pairing_iterator 
: boost::iterator_facade< 
    pairing_iterator<Iterator>, 
    std::array<typename std::iterator_traits<Iterator>::value_type, 2>, 
    std::input_iterator_category 
    // I should probably customize reference too, but it's not relevant 
> { 
    pairing_iterator(Iterator it) : it(it) { 
    } 
    pairing_iterator::value_type dereference() const { 
     auto t = *it++; 
     return { { std::move(t), *it } }; 
    } 
    bool equal(pairing_iterator const& that) const { 
     return it == that.it; 
    } 
    void increment() { 
     ++it; 
    } 
    Iterator it; 
}; 

segunda idea elimina la limitación exactamente un desreferenciar, pero es feo y extraño:

template <typename Iterator> 
struct pairing_iterator 
: boost::iterator_facade< 
    pairing_iterator<Iterator>, 
    std::array<typename std::iterator_traits<Iterator>::value_type, 2>, 
    std::input_iterator_category 
    // I should probably customize reference too, but it's not relevant 
> { 
    pairing_iterator(Iterator it) : it(it), dereferenced(false) { 
    } 
    pairing_iterator::value_type dereference() const { 
     if (!dereferenced) { 
      auto t = *it++; 
      pair = { { std::move(t), *it } }; 
      dereferenced = true; 
     } 
     return pair; 
    } 
    bool equal(pairing_iterator const& that) const { 
     return it == that.it; 
    } 
    void increment() { 
     if (!dereferenced) 
      dereference(); 
     dereferenced = false; 
     ++it; 
    } 
    Iterator it; 
    pairing_iterator::value_type pair; 
    bool dereferenced; 
}; 

que probab Hice varios errores, pero espero que esto sea suficiente para retratar los conceptos. Se requerirían

+0

{{* it ++, * it}}; devolverá el mismo elemento para cada campo (o no está definido) ya que ++ no aparece hasta después del ';' ... – bytemaster

+0

@bytemaster: no estoy seguro de que tenga razón, pero por las dudas, lo arreglé eso. Es un cambio trivial. –

+0

@bytemaster no, el orden de evaluación dentro de '{}' está bien definido. –

-1
template<typename T> 
struct piterator { 
    typedef std::pair<typename std::iterator_traits<T>::value_type, 
        typename std::iterator_traits<T>::value_type> value_type; 

    piterator(){} 
    piterator(const T& t, const T& e):itr(t),eitr(e){ 
    if(itr != eitr) head.first = *itr; 
    if(itr != eitr)head.second = *(++itr); 
    } 
    bool operator ==(const piterator& e)const { 
     return e.itr == itr && e.eitr== eitr; 
    } 

    value_type& operator*(){ return head; } 
    const value_type& operator*()const { return head; } 
    value_type& operator->(){ return head; } 
    const value_type& operator->()const { return head; } 

    piterator& operator++() { 
     if(itr != eitr)head.first = *(++itr); 
     if(itr != eitr)head.second = *(++itr); 
     return *this; 
    } 
    piterator& operator++(int) { 
     if(itr != eitr)head.first = *(++itr); 
     if(itr != eitr)head.second = *(++itr); 
     return *this; 
    } 
    private: 
    T itr; 
    T eitr; 
    value_type head; 
}; 

comprobaciones adicionales para asegurarse de que el iterador emparejamiento no 'ir más allá del final' de una lista de tamaño irregular.

+0

¿Cómo se ve un * end * 'piterator'? AFAICT, simplemente tendrá un comportamiento indefinido directamente del constructor. –

Cuestiones relacionadas