C ++ 11基于反向范围的for循环

    技术2022-07-11  107

    本文翻译自:C++11 reverse range-based for-loop

    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? 是否有一个容器适配器可以颠倒迭代器的方向,以便我可以使用基于范围的for循环反向迭代容器?

    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楼

    参考:https://stackoom.com/question/ZqJj/C-基于反向范围的for循环


    #2楼

    This should work in C++11 without boost: 这应该可以在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)) { ... }

    #3楼

    Actually, in C++14 it can be done with a very few lines of code. 实际上,在C ++ 14中,只需几行代码即可完成。

    This is a very similar in idea to @Paul's solution. 这在思想上与@Paul的解决方案非常相似。 Due to things missing from C++11, that solution is a bit unnecessarily bloated (plus defining in std smells). 由于C ++ 11中缺少某些内容,因此该解决方案有点不必要地过大(加上在std气味中定义)。 Thanks to C++14 we can make it a lot more readable. 感谢C ++ 14,我们可以使它更具可读性。

    The key observation is that ranged-based for-loops work by relying on begin() and end() in order to acquire the range's iterators. 关键观察是基于范围的for循环通过依靠begin()和end()来获取范围的迭代器。 Thanks to ADL , one doesn't even need to define their custom begin() and end() in the std:: namespace. 多亏了ADL ,甚至不需要在std ::名称空间中定义其自定义begin()和end() 。

    Here is a very simple-sample solution: 这是一个非常简单的示例解决方案:

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

    This works like a charm, for instance: 例如,这就像一个咒语一样工作:

    template <typename T> 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<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); print_iterable(cout, reverse(ints_vec)); return 0; }

    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. 注意 std::rbegin() , std::rend()和std::make_reverse_iterator()在GCC-4.9中尚未实现。 I write these examples according to the standard, but they would not compile in stable g++. 我根据标准编写了这些示例,但是它们无法在稳定的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 <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()); }

    #4楼

    If not using C++14, then I find below the simplest solution. 如果不使用C ++ 14,那么我在下面找到最简单的解决方案。

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

    Demo . 演示 It doesn't work for the containers/data-types (like array), which doesn't have begin/rbegin, end/rend functions. 它不适用于没有begin/rbegin, end/rend功能的容器/数据类型(如数组)。


    #5楼

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

    eg: 例如:

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

    #6楼

    You could simply use BOOST_REVERSE_FOREACH which iterates backwards. 您可以简单地使用向后迭代的BOOST_REVERSE_FOREACH 。 For example, the code 例如,代码

    #include <iostream> #include <boost\foreach.hpp> int main() { int integers[] = { 0, 1, 2, 3, 4 }; BOOST_REVERSE_FOREACH(auto i, integers) { std::cout << i << std::endl; } return 0; }

    generates the following output: 生成以下输出:

    4 3 2 1 0
    Processed: 0.010, SQL: 9