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(如数组)。