C ++ 11基于反向范围的for-loop

是否有一个容器适配器,将颠倒迭代器的方向,所以我可以遍历一个容器反向与基于范围的for循环?

用显式的迭代器,我会转换这个:

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

进入这个:

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

我想转换这个:

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

对此:

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

有这样的事情,还是我自己写?

实际上Boost确实有这样的适配器: 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'; } 

实际上,在C ++ 14中,只需要几行代码即可完成。

这与@ Paul的解决scheme非常相似。 由于C ++ 11缺less的东西,这个解决scheme有点不必要的臃肿(加上定义在std的气味)。 感谢C ++ 14,我们可以使其更具可读性。

关键的观察是基于范围的for循环依靠begin()end()来获取范围的迭代器。 感谢ADL ,甚至不需要在std :: namespace中定义自定义的begin()end()

这是一个非常简单的示例解决scheme:

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

这就像一个魅力,例如:

 template <typename T> void print_iterable (ostream& out, const T& iterable) { for (auto&& element: iterable) out << element << ','; cout << '\n'; } int main (int, char**) { // 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) << '\n'; print_iterable(cout, reverse(ints_vec)) << '\n'; return 0; } 

按预期打印

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

注意 std::rbegin()std::rend()std::make_reverse_iterator()在GCC-4.9中尚未实现。 我按照标准写这些例子,但是它们不能用稳定的g ++编译。 尽pipe如此,为这三个function添加临时存根是非常容易的。 下面是一个示例实现, 绝对不是完整的,但对于大多数情况来说工作得很好:

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

更新2017年10月22日

感谢estan指出这一点。

原来的答案示例实现using namespace std; ,这将导致任何包含这个实现的文件(必须在头文件中)也导入整个std命名空间。

修改了示例实现来build议using std::rbegin, std::rend而不是using std::rbegin, std::rend

这应该工作在C + + 11没有提升:

 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)) { ... } 

这是否适合你:

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

例如:

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

如果不使用C ++ 14,那么我在下面find最简单的解决scheme。

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

演示 。
它不适用于没有begin/rbegin, end/rend函数的容器/数据types(如数组)。