C++11 反向基于范围的 for 循环
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/8542591/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
C++11 reverse range-based for-loop
提问by Alex B
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?
有这样的东西还是我必须自己写?
采纳答案by kennytm
Actually Boost does have such adaptor: boost::adaptors::reverse
.
实际上 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';
}
回答by Prikso NAI
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. 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.
这与@Paul 的解决方案的想法非常相似。由于 C++11 中缺少一些东西,该解决方案有点不必要地臃肿(加上在 std 气味中定义)。感谢 C++14,我们可以让它更具可读性。
The key observation is that range-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.
关键的观察是基于范围的 for 循环通过依赖begin()
和end()
获取范围的迭代器来工作。多亏了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,
NOTEstd::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 completebut works well enough for most cases:
注意std::rbegin()
、std::rend()
和std::make_reverse_iterator()
尚未在 GCC-4.9 中实现。我根据标准编写这些示例,但它们不会在稳定的 g++ 中编译。尽管如此,为这三个函数添加临时存根非常容易。这是一个示例实现,绝对不完整,但在大多数情况下运行良好:
// --------------------------------------------------
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());
}
回答by Paul Fultz II
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))
{
...
}
回答by Arlen
Does this work for you:
这对你有用吗:
#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;
}
回答by Khan Lau
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;
}
回答by P.W
If you can use range v3, you can use the reverse range adapter ranges::view::reverse
which allows you to view the container in reverse.
如果您可以使用range v3,则可以使用反向范围适配器ranges::view::reverse
,它允许您反向查看容器。
A minimal working example:
一个最小的工作示例:
#include <iostream>
#include <vector>
#include <range/v3/view.hpp>
int main()
{
std::vector<int> intVec = {1, 2, 3, 4, 5, 6, 7, 8, 9};
for (auto const& e : ranges::view::reverse(intVec)) {
std::cout << e << " ";
}
std::cout << std::endl;
for (auto const& e : intVec) {
std::cout << e << " ";
}
std::cout << std::endl;
}
See DEMO 1.
见演示 1。
Note:As per Eric Niebler, this feature will be available in C++20. This can be used with the <experimental/ranges/range>
header. Then the for
statement will look like this:
注意:根据Eric Niebler 的说法,此功能将在C++20 中可用。这可以与<experimental/ranges/range>
标题一起使用。然后for
语句将如下所示:
for (auto const& e : view::reverse(intVec)) {
std::cout << e << " ";
}
See DEMO 2
见演示 2
回答by iammilind
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
函数的容器/数据类型(如数组)。
回答by Catriel
You could simply use BOOST_REVERSE_FOREACH
which iterates backwards. For example, the code
您可以简单地使用BOOST_REVERSE_FOREACH
which 向后迭代。例如,代码
#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