C ++ 11 Reverse-Range-basierte For-Schleife

Gibt es einen Container-Adapter, der die Richtung der Iteratoren umkehren würde, so dass ich mit einer bereichsbasierten For-Schleife rückwärts über einen Container iterieren kann?

Mit expliziten Iteratoren würde ich das konvertieren:

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

das mögen:

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

Ich möchte das konvertieren:

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

zu diesem:

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

Gibt es so etwas oder muss ich es selbst schreiben?

   

Eigentlich hat Boost einen solchen Adapter: boost::adaptors::reverse adapter boost::adaptors::reverse .

 #include  #include  #include  int main() { std::list 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'; } 

Tatsächlich kann in C ++ 14 mit sehr wenigen Codezeilen gearbeitet werden.

Dies ähnelt der Idee von Paul sehr. Aufgrund von Dingen, die in C ++ 11 fehlen, ist diese Lösung ein wenig unnötig aufgebläht (und definiert zusätzlich in Standardgerüchen). Dank C ++ 14 können wir es viel lesbarer machen.

Die wichtigste Beobachtung besteht darin, dass forward-basierte for-loops arbeiten, indem sie sich auf begin() und end() verlassen, um die Iteratoren des Bereichs zu erhalten. Dank ADL muss man nicht einmal ihre eigenen begin() und end() im Namespace std :: definieren.

Hier ist eine sehr einfache Beispiellösung:

 // ------------------------------------------------------------------- // --- Reversed iterable template  struct reversion_wrapper { T& iterable; }; template  auto begin (reversion_wrapper w) { return std::rbegin(w.iterable); } template  auto end (reversion_wrapper w) { return std::rend(w.iterable); } template  reversion_wrapper reverse (T&& iterable) { return { iterable }; } 

Das funktioniert wie ein Zauber, zum Beispiel:

 template  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 { 1, 2, 3, 4, })); // on const lvalue references const list ints_list { 1, 2, 3, 4, }; for (auto&& el: reverse(ints_list)) cout < < el << ','; cout << '\n'; // on mutable lvalue references vector 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; } 

druckt wie erwartet

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

HINWEIS std::rbegin() , std::rend() und std::make_reverse_iterator() sind in GCC-4.9 noch nicht implementiert. Ich schreibe diese Beispiele nach dem Standard, aber sie würden nicht in stabilem g ++ kompilieren. Dennoch ist das Hinzufügen temporärer Stubs für diese drei functionen sehr einfach. Hier ist eine Beispielimplementierung, definitiv nicht vollständig, aber funktioniert gut genug für die meisten Fälle:

 // -------------------------------------------------- template  reverse_iterator make_reverse_iterator (I i) { return std::reverse_iterator { i }; } // -------------------------------------------------- template  auto rbegin (T& iterable) { return make_reverse_iterator(iterable.end()); } template  auto rend (T& iterable) { return make_reverse_iterator(iterable.begin()); } // const container variants template  auto rbegin (const T& iterable) { return make_reverse_iterator(iterable.end()); } template  auto rend (const T& iterable) { return make_reverse_iterator(iterable.begin()); } 

UPDATE 22. Oktober 2017

Danke an estan dafür, dass er darauf hingewiesen hat.

Die ursprüngliche Antwort Beispiel Implementierung verwendet using namespace std; , die dazu führen würde, dass jede Datei, die diese Implementierung enthält (die in der Header-Datei enthalten sein muss), auch den gesamten std Namespace importiert.

Überarbeitete die Beispielimplementierung, um using std::rbegin, std::rend vorzuschlagen.

Dies sollte in C ++ 11 ohne Boost funktionieren:

 namespace std { template T begin(std::pair p) { return p.first; } template T end(std::pair p) { return p.second; } } template std::reverse_iterator make_reverse_iterator(Iterator it) { return std::reverse_iterator(it); } template std::pair()))>, std::reverse_iterator()))>> 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)) { ... } 

Geht das für dich:

 #include  #include  #include  #include  #include  int main(int argc, char* argv[]){ typedef std::list Nums; typedef Nums::iterator NumIt; typedef boost::range_reverse_iterator::type RevNumIt; typedef boost::iterator_range irange_1; typedef boost::iterator_range 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; } 
  template  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  struct reverse_wrapper< C[N] >{ C (&c_)[N]; reverse_wrapper( C(&c)[N] ) : c_(c) {} typename std::reverse_iterator begin() { return std::rbegin(c_); } typename std::reverse_iterator end() { return std::rend(c_); } }; template  reverse_wrapper r_wrap(C & c) { return reverse_wrapper(c); } 

z.B:

  int main(int argc, const char * argv[]) { std::vector 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; } 

Wenn ich C ++ 14 nicht verwende, dann finde ich unten die einfachste Lösung.

 #define METHOD(NAME, ...) auto NAME __VA_ARGS__ -> decltype(m_T.r##NAME) { return m_T.r##NAME; } template struct Reverse { T& m_T; METHOD(begin()); METHOD(end()); METHOD(begin(), const); METHOD(end(), const); }; #undef METHOD template Reverse MakeReverse (T& t) { return Reverse{t}; } 

Demo .
Es funktioniert nicht für die Container / Datentypen (wie array), die keine begin/rbegin, end/rend functionen haben.