instead - C++ 11 for-loop basado en rango inverso
use for loop in c++ (6)
En realidad, Boost tiene ese 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'';
}
¿Hay 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 escribirla yo mismo?
En realidad, en C ++ 14 se puede hacer con muy pocas líneas de código.
Esta es una idea muy similar a la de @ Paul. Debido a las cosas que faltan en C ++ 11, esa solución está un poco innecesariamente hinchada (más la definición en los olores estándar). Gracias a C ++ 14 podemos hacerlo mucho más legible.
La observación clave es que los for-loops basados en rangos funcionan confiando en begin()
y end()
para adquirir los iteradores del rango. Gracias a ADL , uno ni siquiera necesita definir su begin()
y su end()
en el espacio de nombres std ::.
Aquí hay una solución muy simple de muestra:
// -------------------------------------------------------------------
// --- 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 como un encanto, 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 espera
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 apéndices temporales para estas tres funciones es muy fácil. Aquí hay una implementación de muestra, definitivamente no está completa 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());
}
ACTUALIZACIÓN 22 oct 2017
Gracias a estan por señalar esto.
La implementación de ejemplo de respuesta original utiliza using namespace std;
, lo que causaría que cualquier archivo que incluya esta implementación (que debe estar en el archivo de cabecera), también importe todo el std
nombres std
.
Se revisó la implementación de muestra para proponer el using std::rbegin, std::rend
lugar.
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))
{
...
}
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;
}
Si no utilizo C ++ 14, a continuación encuentro la solución más simple.
#define METHOD(NAME, ...) auto NAME __VA_ARGS__ -> decltype(m_T.r##NAME) { return m_T.r##NAME; }
template<typename T>
struct Reverse
{
T& m_T;
METHOD(begin());
METHOD(end());
METHOD(begin(), const);
METHOD(end(), const);
};
#undef METHOD
template<typename T>
Reverse<T> MakeReverse (T& t) { return Reverse<T>{t}; }
Demo
No funciona para los contenedores / tipos de datos (como matriz), que no tiene begin/rbegin, end/rend
.
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;
}