C++11 reverse range-based for-loop

匿名 (未验证) 提交于 2019-12-03 02:11:02

问题:

Is there a container adapter that would reverse the direction of iterators so I can iterate over a container in reverse with range-based for-loop?

With explicit iterators I would convert this:

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

into this:

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

I want to convert this:

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

to this:

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

Is there such a thing or do I have to write it myself?

回答1:

Actually Boost does have such adaptor: 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 


回答2:

Actually, in C++14 it can be done with a very few lines of code.

This is a very similar in idea to @Paul's solution. Due to things missing from C++11, that solution is a bit unnecessarily bloated (plus defining in std smells). Thanks to C++14 we can make it a lot more readable.

The key observation is that ranged-based for-loops work by relying on begin() and end() in order to acquire the range's iterators. Thanks to ADL, one doesn't even need to define their custom begin() and end() in the std:: namespace.

Here is a very simple-sample solution:

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

This works like a charm, for instance:

template  void print_iterable (ostream& out, const T& iterable) {     for (auto&& element: iterable)         out  { 1, 2, 3, 4, }));      // on const lvalue references     const list ints_list { 1, 2, 3, 4, };     for (auto&& el: reverse(ints_list))         cout  ints_vec { 0, 0, 0, 0, };     size_t i = 0;     for (int& el: reverse(ints_vec))         el += i++;     print_iterable(cout, ints_vec) 

prints as expected

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

NOTE std::rbegin(), std::rend(), and std::make_reverse_iterator() are not yet implemented in GCC-4.9. I write these examples according to the standard, but they would not compile in stable g++. Nevertheless, adding temporary stubs for these three functions is very easy. Here is a sample implementation, definitely not complete but works well enough for most cases:

// -------------------------------------------------- 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 Oct 2017

Thanks to estan for pointing this out.

The original answer sample implementation uses using namespace std;, which would cause any file including this implementation (that has to be in header file), to also import the whole std namespace.

Revised the sample implementation to propose using std::rbegin, std::rend instead.



回答3:

This should work in C++11 without boost:

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<:reverse_iterator>()))>, 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)) {     ... } 


回答4:

Does this work for you:

#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 


回答5:

    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 (&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);     } 

eg:

    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;     } 


回答6:

If not using C++14, then I find below the simplest solution.

#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.
It doesn't work for the containers/data-types (like array), which doesn't have begin/rbegin, end/rend functions.



标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!