C ++ 11 for-loop basado en rango inverso

11 minutos de lectura

C 11 for loop basado en rango inverso
alex b

¿Existe un adaptador de contenedor que invierta la dirección de los iteradores para que pueda iterar sobre un contenedor a la inversa con un bucle for basado en rango?

Con iteradores explícitos convertiría esto:

for (auto i = c.begin(); i != c.end(); ++i) { ...

dentro de esto:

for (auto i = c.rbegin(); i != c.rend(); ++i) { ...

Quiero convertir esto:

for (auto& i: c) { ...

a esto:

for (auto& i: std::magic_reverse_adapter(c)) { ...

¿Existe tal cosa o tengo que escribirlo yo mismo?

  • Un adaptador de contenedor inverso, suena interesante, pero creo que tendrás que escribirlo tú mismo. No tendríamos este problema si el comité de estándares se apresurara y adaptara algoritmos basados ​​en rangos en lugar de iteradores explícitos.

    – código_deft

    17 de diciembre de 2011 a las 4:34

  • @deft_code: “¿en lugar de?” ¿Por qué querrías deshacerte de los algoritmos basados ​​en iteradores? Son mucho mejores y menos detallados para los casos en los que no itera desde begin para end, o para tratar con iteradores de flujo y similares. Los algoritmos de rango serían geniales, pero en realidad son solo azúcar sintáctico (excepto por la posibilidad de una evaluación perezosa) sobre algoritmos iteradores.

    – Nicolás Bolas

    17 de diciembre de 2011 a las 4:41

  • @deft_code template<typename T> class reverse_adapter { public: reverse_adapter(T& c) : c(c) { } typename T::reverse_iterator begin() { return c.rbegin(); } typename T::reverse_iterator end() { return c.rend(); } private: T& c; }; Se puede mejorar (agregando const versiones, etc) pero funciona: vector<int> v {1, 2, 3}; reverse_adapter<decltype(v)> ra; for (auto& i : ra) cout << i; huellas dactilares 321

    – Seth Carnegie

    17 de diciembre de 2011 a las 4:56


  • @SethCarnegie: Y para agregar una buena forma funcional: template<typename T> reverse_adapter<T> reverse_adapt_container(T &c) {return reverse_adapter<T>(c);} Entonces puedes usar for(auto &i: reverse_adapt_container(v)) cout << i; para iterar.

    – Nicolás Bolas

    17 de diciembre de 2011 a las 5:31

  • @CR: No lo creo deberían significa eso, porque eso haría que no esté disponible como una sintaxis concisa para bucles donde el orden sí importa. En mi opinión, la concisión es más importante/útil que su significado semántico, pero si no valora la concisión de su guía de estilo, puede darle la implicación que desee. Eso es algo de lo que parallel_for sería para, con una condición aún más fuerte de “No me importa en qué orden”, si se incorporara al estándar de alguna forma. Por supuesto, también podría tener un azúcar sintáctico basado en rangos 🙂

    –Steve Jessop

    06/03/2014 a las 14:30


C 11 for loop basado en rango inverso
kennytm

En realidad, Boost tiene dicho adaptador: boost::adaptors::reverse.

#include <list>
#include <iostream>
#include <boost/range/adaptor/reversed.hpp>

int main()
{
    std::list<int> x { 2, 3, 5, 7, 11, 13, 17, 19 };
    for (auto i : boost::adaptors::reverse(x))
        std::cout << i << '\n';
    for (auto i : x)
        std::cout << i << '\n';
}

1646966588 332 C 11 for loop basado en rango inverso
Prikso NAI

En realidad, en C++14 se puede hacer con muy pocas líneas de código.

Esta es una idea muy similar a la solución de @Paul. Debido a cosas que faltan en C++ 11, esa solución está un poco inflada innecesariamente (además de definir olores estándar). Gracias a C++14 podemos hacerlo mucho más legible.

La observación clave es que los bucles for basados ​​en rango funcionan basándose en begin() y end() para adquirir los iteradores del rango. Gracias a AVDuno ni siquiera necesita definir su costumbre begin() y end() en el espacio de nombres std::.

Aquí hay una solución de muestra muy simple:

// -------------------------------------------------------------------
// --- Reversed iterable

template <typename T>
struct reversion_wrapper { T& iterable; };

template <typename T>
auto begin (reversion_wrapper<T> w) { return std::rbegin(w.iterable); }

template <typename T>
auto end (reversion_wrapper<T> w) { return std::rend(w.iterable); }

template <typename T>
reversion_wrapper<T> reverse (T&& iterable) { return { iterable }; }

Esto funciona de maravilla, por ejemplo:

template <typename T>
void print_iterable (std::ostream& out, const T& iterable)
{
    for (auto&& element: iterable)
        out << element << ',';
    out << '\n';
}

int main (int, char**)
{
    using namespace std;

    // on prvalues
    print_iterable(cout, reverse(initializer_list<int> { 1, 2, 3, 4, }));

    // on const lvalue references
    const list<int> ints_list { 1, 2, 3, 4, };
    for (auto&& el: reverse(ints_list))
        cout << el << ',';
    cout << '\n';

    // on mutable lvalue references
    vector<int> ints_vec { 0, 0, 0, 0, };
    size_t i = 0;
    for (int& el: reverse(ints_vec))
        el += i++;
    print_iterable(cout, ints_vec);
    print_iterable(cout, reverse(ints_vec));

    return 0;
}

imprime como se esperaba

4,3,2,1,
4,3,2,1,
3,2,1,0,
0,1,2,3,

NOTA std::rbegin(), std::rend()y std::make_reverse_iterator() aún no están implementados en GCC-4.9. Escribo estos ejemplos de acuerdo con el estándar, pero no se compilarían en g ++ estable. Sin embargo, agregar stubs temporales para estas tres funciones es muy fácil. Aquí hay una implementación de muestra, definitivamente no completo pero funciona lo suficientemente bien para la mayoría de los casos:

// --------------------------------------------------
template <typename I>
reverse_iterator<I> make_reverse_iterator (I i)
{
    return std::reverse_iterator<I> { i };
}

// --------------------------------------------------
template <typename T>
auto rbegin (T& iterable)
{
    return make_reverse_iterator(iterable.end());
}

template <typename T>
auto rend (T& iterable)
{
    return make_reverse_iterator(iterable.begin());
}

// const container variants

template <typename T>
auto rbegin (const T& iterable)
{
    return make_reverse_iterator(iterable.end());
}

template <typename T>
auto rend (const T& iterable)
{
    return make_reverse_iterator(iterable.begin());
}

  • ¿Pocas líneas de código? Perdóname pero eso es más de diez 🙂

    – Jonny

    27 de abril de 2016 a las 1:54

  • En realidad, son 5-13, dependiendo de cómo cuentes las líneas 🙂 Las soluciones alternativas no deberían estar allí, ya que son parte de la biblioteca. Gracias por recordarme, por cierto, esta respuesta debe actualizarse para versiones recientes del compilador, donde todas las líneas adicionales no son necesarias en absoluto.

    – Prikso NAI

    27 de abril de 2016 a las 12:37

  • creo que te olvidaste forward<T> en tus reverse implementación.

    – Serpiente

    25 de noviembre de 2016 a las 12:11

  • Hm, si pones esto en un encabezado, estás using namespace std en un encabezado, lo cual no es una buena idea. ¿O me estoy perdiendo algo?

    – estan

    21 de octubre de 2017 a las 9:46

  • En realidad, no deberías estar escribiendo “usando ;” en el alcance del archivo en un encabezado. Podría mejorar lo anterior moviendo las declaraciones de uso al alcance de la función para begin() y end().

    – Chris Hartmann

    22 de abril de 2018 a las 17:56

1646966589 425 C 11 for loop basado en rango inverso
Pablo Fultz II

Esto debería funcionar en C++ 11 sin impulso:

namespace std {
template<class T>
T begin(std::pair<T, T> p)
{
    return p.first;
}
template<class T>
T end(std::pair<T, T> p)
{
    return p.second;
}
}

template<class Iterator>
std::reverse_iterator<Iterator> make_reverse_iterator(Iterator it)
{
    return std::reverse_iterator<Iterator>(it);
}

template<class Range>
std::pair<std::reverse_iterator<decltype(begin(std::declval<Range>()))>, std::reverse_iterator<decltype(begin(std::declval<Range>()))>> make_reverse_range(Range&& r)
{
    return std::make_pair(make_reverse_iterator(begin(r)), make_reverse_iterator(end(r)));
}

for(auto x: make_reverse_range(r))
{
    ...
}

  • IIRC agregar cualquier cosa al espacio de nombres estándar es una invitación a la falla épica.

    – BCS

    18 de septiembre de 2013 a las 1:33

  • No estoy seguro del significado normativo de “fallo épico”, pero sobrecargar una función en el std el espacio de nombres tiene un comportamiento indefinido según 17.6.4.2.1.

    – Casey

    6 de marzo de 2014 a las 15:34

  • Está dentro C ++ 14 aparentementebajo este nombre.

    – HostileFork dice que no confíes en SE

    14 de diciembre de 2014 a las 23:42

  • @MuhammadAnnaqeeb Lo desafortunado es que hacerlo choca exactamente. No se puede compilar con ambas definiciones. Además, no se requiere que el compilador tenga la definición. no estar presente en C++11 y solo aparecer en C++14 (la especificación no dice nada sobre lo que está no en el espacio de nombres std::, justo lo que es). Así que esto sería una falla de compilación muy probable bajo un compilador C++ 11 compatible con los estándares… mucho más probable que si fuera un nombre aleatorio que no fue en C++14! Y como se señaló, es un “comportamiento indefinido” … por lo que fallar en la compilación no es lo peor que podría hacer.

    – HostileFork dice que no confíes en SE

    28 de febrero de 2015 a las 4:26


  • @HostileFork No hay colisión de nombres, make_reverse_iterator no está en el std espacio de nombres, por lo que no chocará con la versión C++ 14 del mismo.

    –Paul Fultz II

    29 de julio de 2015 a las 21:04

si puedes usar rango v3 puede usar el adaptador de rango inverso ranges::view::reverse que le permite ver el contenedor al revés.

Un ejemplo de trabajo mínimo:

#include <iostream>
#include <vector>
#include <range/v3/view.hpp>

int main()
{
    std::vector<int> intVec = {1, 2, 3, 4, 5, 6, 7, 8, 9};

    for (auto const& e : ranges::view::reverse(intVec)) {
        std::cout << e << " ";   
    }
    std::cout << std::endl;

    for (auto const& e : intVec) {
        std::cout << e << " ";   
    }
    std::cout << std::endl;
}

Ver DEMO 1.

Nota: según eric niebleresta característica estará disponible en C++20. Esto se puede utilizar con el <experimental/ranges/range> encabezamiento. Entonces el for declaración se verá así:

for (auto const& e : view::reverse(intVec)) {
       std::cout << e << " ";   
}

Ver DEMO 2

Obtuve este ejemplo de preferencia cp. Funciona con:

GCC 10.1+ con indicador -std=c++20

#include <ranges>
#include <iostream>
 
int main()
{
    static constexpr auto il = {3, 1, 4, 1, 5, 9};
 
    std::ranges::reverse_view rv {il};
    for (int i : rv)
        std::cout << i << ' ';
 
    std::cout << '\n';
 
    for(int i : il | std::views::reverse)
        std::cout << i << ' ';
}

1646966589 23 C 11 for loop basado en rango inverso
arlen

Esto funciona para tí:

#include <iostream>
#include <list>
#include <boost/range/begin.hpp>
#include <boost/range/end.hpp>
#include <boost/range/iterator_range.hpp>

int main(int argc, char* argv[]){

  typedef std::list<int> Nums;
  typedef Nums::iterator NumIt;
  typedef boost::range_reverse_iterator<Nums>::type RevNumIt;
  typedef boost::iterator_range<NumIt> irange_1;
  typedef boost::iterator_range<RevNumIt> irange_2;

  Nums n = {1, 2, 3, 4, 5, 6, 7, 8};
  irange_1 r1 = boost::make_iterator_range( boost::begin(n), boost::end(n) );
  irange_2 r2 = boost::make_iterator_range( boost::end(n), boost::begin(n) );


  // prints: 1 2 3 4 5 6 7 8 
  for(auto e : r1)
    std::cout << e << ' ';

  std::cout << std::endl;

  // prints: 8 7 6 5 4 3 2 1
  for(auto e : r2)
    std::cout << e << ' ';

  std::cout << std::endl;

  return 0;
}

1646966589 692 C 11 for loop basado en rango inverso
Vivek jainista

template <typename C>
struct reverse_wrapper {

    C & c_;
    reverse_wrapper(C & c) :  c_(c) {}

    typename C::reverse_iterator begin() {return c_.rbegin();}
    typename C::reverse_iterator end() {return c_.rend(); }
};

template <typename C, size_t N>
struct reverse_wrapper< C[N] >{

    C (&c_)[N];
    reverse_wrapper( C(&c)[N] ) : c_(c) {}

    typename std::reverse_iterator<const C *> begin() { return std::rbegin(c_); }
    typename std::reverse_iterator<const C *> end() { return std::rend(c_); }
};


template <typename C>
reverse_wrapper<C> r_wrap(C & c) {
    return reverse_wrapper<C>(c);
}

p.ej:

int main(int argc, const char * argv[]) {
    std::vector<int> arr{1, 2, 3, 4, 5};
    int arr1[] = {1, 2, 3, 4, 5};

    for (auto i : r_wrap(arr)) {
        printf("%d ", i);
    }
    printf("\n");

    for (auto i : r_wrap(arr1)) {
        printf("%d ", i);
    }
    printf("\n");
    return 0;
}

  • ¿Puede explicar más detalladamente su respuesta?

    – Mostafíz

    29 de abril de 2016 a las 3:27

  • este es un tamplate de clase C ++ 11 de bucle base de rango inverso

    – Khan Lau

    13 de mayo de 2016 a las 9:01


¿Ha sido útil esta solución?

Esta web utiliza cookies propias y de terceros para su correcto funcionamiento y para fines analíticos y para mostrarte publicidad relacionada con sus preferencias en base a un perfil elaborado a partir de tus hábitos de navegación. Al hacer clic en el botón Aceptar, acepta el uso de estas tecnologías y el procesamiento de tus datos para estos propósitos. Configurar y más información
Privacidad