可以将文章内容翻译成中文,广告屏蔽插件可能会导致该功能失效(如失效,请关闭广告屏蔽插件后再试):
问题:
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.