Tengo una clase que contiene y gestiona una serie de objetos. Para evitar filtrar cómo se almacenan estos objetos mientras se permite iterar a través de ellos, decidí usar el borrado de tipo usando boost::any_iterator.

 using my_erased_type_iterator = boost::range_detail::any_iterator<
    MyClass,
    boost::bidirectional_traversal_tag,
    MyClass&, 
    std::ptrdiff_t>;

Definí una función Begin() y End() en MyClass, que simplemente devuelve la función begin() y end() del contenedor como my_erased_type_iterator. Funciona exactamente como quiero, y nadie fuera de MyClass sabe que estoy usando un vector para almacenar los objetos ni tienen acceso al contenedor aparte de las funciones que expongo en Myclass interfaz.

Ahora, por varias razones, necesito realizar una iteración inversa a través de los objetos. También necesito saber el siguiente elemento después de un iterador inverso (similar a llamar a std::next() en un iterador normal, que ya no es tan trivial para los iteradores inversos) y es posible que también necesite llamar a funciones como erase() en ese iterador inverso.

Entonces, para mi pregunta: ¿hay una forma elegante de usar el borrado de tipo junto con los iteradores inversos (y la versión constante de avance y retroceso)? ¿Debo usar iteradores de tipo borrado hacia adelante e iterar hacia atrás en su lugar? Se me pasó por la cabeza que podría estar abordando este problema de manera incorrecta, así que estoy abierto a cualquier sugerencia o aclarar mis preguntas si es necesario.

4
user1784377 27 ene. 2016 a las 01:43

2 respuestas

La mejor respuesta

Tenga en cuenta que any_iterator es un detalle de implementación.

Primero responderé a su pregunta directa, luego mostraré el enfoque con any_range<> según lo previsto por la API pública de Boost Range.

1. make_reverse_iterator

Simplemente puede usar la función make_reverse_iterator de

Live On Coliru

#include <boost/range.hpp>
#include <boost/range/any_range.hpp>

struct MyClass {
    int i;
};

using my_erased_type_iterator = boost::range_detail::any_iterator<
    MyClass,
    boost::bidirectional_traversal_tag,
    MyClass&, 
    std::ptrdiff_t>;

#include <iostream>
#include <vector>

int main() {
    using namespace boost;
    std::vector<MyClass> const v { {1}, {2}, {3}, {4} };

    for (auto& mc : make_iterator_range(
                make_reverse_iterator(v.end()),
                make_reverse_iterator(v.begin())))
    {
        std::cout << mc.i << " ";
    }
}

Huellas dactilares

4 3 2 1 

2. Adaptador de rango reversed:

Alternativamente, puede ir al estilo de rango completo y usar any_range<>:

Live On Coliru

int main() {
    std::vector<MyClass> const v { {1}, {2}, {3}, {4} };

    boost::any_range_type_generator<decltype(v)>::type x = reverse(v);

    for (my_erased_type_const_iterator f = boost::begin(x), l = boost::end(x); f!=l; ++f) {
        std::cout << f->i << " ";
    }

}
1
sehe 27 ene. 2016 a las 00:00

Simplemente invierta el tipo de iteradores borrados.

Esto expone .base(), lo que significa que borrar es casi tan fácil como borrar los tipos borrados hacia adelante.

Además, su diseño tiene costos de rendimiento para (en mi experiencia) un beneficio marginal. Las reglas de iterafor invalidadion del contenedor subyacente aún se aplican, por lo que el usuario de su clase tiene que saber cuál es el contenedor subyacente de todos modos. (O saben tanto que ellos también podrían). Cambiar el contenedor no proporcionará un comportamiento suficientemente similar, por lo que su contenedor está bloqueado a pesar de su intento razonablemente costoso de ocultarlo.

1
Yakk - Adam Nevraumont 27 ene. 2016 a las 01:27