是否可以从头到尾迭代一个向量?
for (vector<my_class>::iterator i = my_vector.end();
i != my_vector.begin(); /* ?! */ ) {
}
或者只有这样的事情才有可能:
for (int i = my_vector.size() - 1; i >= 0; --i) {
}
一种方法是:
for (vector<my_class>::reverse_iterator i = my_vector.rbegin();
i != my_vector.rend(); ++i ) {
}
rbegin()
/rend()
专为此目的而设计。 (是的,增加 reverse_interator
会将其向后移动。)
现在,理论上,您的方法(使用 begin()
/end()
和 --i
)可以工作,std::vector
的迭代器是双向的,但请记住,end()
不是最后一个元素 - 它是超越的最后一个元素,所以你必须先递减,当你到达 begin()
时你就完成了——但你仍然需要进行处理。
vector<my_class>::iterator i = my_vector.end();
while (i != my_vector.begin())
{
--i;
/*do stuff */
}
更新:在将 for()
循环重写为 while()
循环时,我显然过于激进。 (重要的是 --i
在开头。)
如果您有 C++11,则可以使用 auto
。
for (auto it = my_vector.rbegin(); it != my_vector.rend(); ++it)
{
}
通过封闭开放范围进行反向迭代的成熟“模式”如下所示
// Iterate over [begin, end) range in reverse
for (iterator = end; iterator-- != begin; ) {
// Process `*iterator`
}
或者,如果您愿意,
// Iterate over [begin, end) range in reverse
for (iterator = end; iterator != begin; ) {
--iterator;
// Process `*iterator`
}
此模式很有用,例如,对于使用无符号索引对数组进行反向索引
int array[N];
...
// Iterate over [0, N) range in reverse
for (unsigned i = N; i-- != 0; ) {
array[i]; // <- process it
}
(不熟悉这种模式的人经常坚持使用有符号整数类型进行数组索引,特别是因为他们错误地认为无符号类型在某种程度上“无法用于”反向索引)
它可用于使用“滑动指针”技术迭代数组
// Iterate over [array, array + N) range in reverse
for (int *p = array + N; p-- != array; ) {
*p; // <- process it
}
或者它可以用于使用普通(非反向)迭代器对向量进行反向迭代
for (vector<my_class>::iterator i = my_vector.end(); i-- != my_vector.begin(); ) {
*i; // <- process it
}
--end()
end()
访问。尽管它们似乎从 end()
开始,但它们始终确保在第一次访问之前递减迭代器。
auto a = vector<int>{0,1,2}; bool reversed = 0; auto it = (!reversed?a.begin():a.end()); auto end = (reversed?a.begin():a.end());
while(it != end) { if(reversed)--it; cout << *it << endl; if(!reversed)++it; }
reversed
四次 次——其中两次在一个循环中。当然,测试一个布尔值是非常快的,但是,为什么你不需要工作呢?特别是,因为唯一的目的似乎是使代码不可读。我们如何使用两个单独的循环? if (reversed) for (auto it = my_vector.rbegin(); it != my_vector.rend(); ++it) {doStuff(*it);} else for (auto it = my_vector.begin(); it != my_vector.end(); ++it) {doStuff(*it);}
if
是绝对正确的,但我想摆脱 doStuff()
上的模板。尽管使用两个 if
仍然可以通过在第一个上循环以相反的方式进行。
从 c++20 开始,您可以使用 std::ranges::reverse_view
和基于范围的 for 循环:
#include<ranges>
#include<vector>
#include<iostream>
using namespace std::ranges;
std::vector<int> const vec{1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
for(auto& i : views::reverse(vec)) {
std::cout << i << ",";
}
甚至
for(auto& i : vec | views::reverse)
不幸的是,在撰写本文时(2020 年 1 月),没有主要编译器实现范围库,但您可以求助于 Eric Niebler's ranges-v3:
#include <iostream>
#include <vector>
#include "range/v3/all.hpp"
int main() {
using namespace ranges;
std::vector<int> const vec{1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
for(auto& i : views::reverse(vec)) {
std::cout << i << ",";
}
return 0;
}
for(auto& i : vec | views::reverse)
感到困惑。它是如何工作的? |
在这里做什么?
用户 rend() / rbegin()
迭代器:
for (vector<myclass>::reverse_iterator it = myvector.rbegin(); it != myvector.rend(); it++)
使用反向迭代器并从 rbegin()
循环到 rend()
template<class It>
std::reverse_iterator<It> reversed( It it ) {
return std::reverse_iterator<It>(std::forward<It>(it));
}
然后:
for( auto rit = reversed(data.end()); rit != reversed(data.begin()); ++rit ) {
std::cout << *rit;
或者在 C++14 中,只需执行以下操作:
for( auto rit = std::rbegin(data); rit != std::rend(data); ++rit ) {
std::cout << *rit;
在 C++03/11 中,大多数标准容器也有 .rbegin()
和 .rend()
方法。
最后,您可以编写范围适配器 backwards
,如下所示:
namespace adl_aux {
using std::begin; using std::end;
template<class C>
decltype( begin( std::declval<C>() ) ) adl_begin( C&& c ) {
return begin(std::forward<C>(c));
}
template<class C>
decltype( end( std::declval<C>() ) ) adl_end( C&& c ) {
return end(std::forward<C>(c));
}
}
template<class It>
struct simple_range {
It b_, e_;
simple_range():b_(),e_(){}
It begin() const { return b_; }
It end() const { return e_; }
simple_range( It b, It e ):b_(b), e_(e) {}
template<class OtherRange>
simple_range( OtherRange&& o ):
simple_range(adl_aux::adl_begin(o), adl_aux::adl_end(o))
{}
// explicit defaults:
simple_range( simple_range const& o ) = default;
simple_range( simple_range && o ) = default;
simple_range& operator=( simple_range const& o ) = default;
simple_range& operator=( simple_range && o ) = default;
};
template<class C>
simple_range< decltype( reversed( adl_aux::adl_begin( std::declval<C&>() ) ) ) >
backwards( C&& c ) {
return { reversed( adl_aux::adl_end(c) ), reversed( adl_aux::adl_begin(c) ) };
}
现在你可以这样做了:
for (auto&& x : backwards(ctnr))
std::cout << x;
我认为这很漂亮。
我喜欢 Yakk 结尾的向后迭代器 - Adam Nevraumont 的回答,但对于我需要的东西来说似乎很复杂,所以我写了这个:
template <class T>
class backwards {
T& _obj;
public:
backwards(T &obj) : _obj(obj) {}
auto begin() {return _obj.rbegin();}
auto end() {return _obj.rend();}
};
我可以像这样使用普通的迭代器:
for (auto &elem : vec) {
// ... my useful code
}
并将其更改为此以反向迭代:
for (auto &elem : backwards(vec)) {
// ... my useful code
}
如果您可以使用 Boost 库,那么 Boost.Range 通过包括以下内容来提供 reverse
range adapter:
#include <boost/range/adaptor/reversed.hpp>
然后,结合 C++11's range-for
loop,您可以编写以下内容:
for (auto& elem: boost::adaptors::reverse(my_vector)) {
// ...
}
由于此代码比使用迭代器对的代码更简洁,因此它可能更具可读性且不易出错,因为需要注意的细节较少。
boost::adaptors::reverse
非常有用!
这是一个超级简单的实现,它允许使用 for each 构造并且仅依赖于 C++14 标准库:
namespace Details {
// simple storage of a begin and end iterator
template<class T>
struct iterator_range
{
T beginning, ending;
iterator_range(T beginning, T ending) : beginning(beginning), ending(ending) {}
T begin() const { return beginning; }
T end() const { return ending; }
};
}
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// usage:
// for (auto e : backwards(collection))
template<class T>
auto backwards(T & collection)
{
using namespace std;
return Details::iterator_range(rbegin(collection), rend(collection));
}
这适用于提供 rbegin() 和 rend() 的东西,以及静态数组。
std::vector<int> collection{ 5, 9, 15, 22 };
for (auto e : backwards(collection))
;
long values[] = { 3, 6, 9, 12 };
for (auto e : backwards(values))
;
使用此代码
//print the vector element in reverse order by normal iterator.
cout <<"print the vector element in reverse order by normal iterator." <<endl;
vector<string>::iterator iter=vec.end();
--iter;
while (iter != vec.begin())
{
cout << *iter << " ";
--iter;
}
vec
指的是空向量,此代码将严重失败!
由于我不想引入类似外星人的新 C++ 语法,而我只是想在现有原语的基础上进行构建,因此以下代码段似乎有效:
#include <vector>
#include <iostream>
int main (int argc,char *argv[])
{
std::vector<int> arr{1,2,3,4,5};
std::vector<int>::iterator it;
// iterate forward
for (it = arr.begin(); it != arr.end(); it++) {
std::cout << *it << " ";
}
std::cout << "\n************\n";
if (arr.size() > 0) {
// iterate backward, simple Joe version
it = arr.end() - 1;
while (it != arr.begin()) {
std::cout << *it << " ";
it--;
}
std::cout << *it << " ";
}
// iterate backwards, the C++ way
std::vector<int>::reverse_iterator rit;
for (rit = arr.rbegin(); rit != arr.rend(); rit++) {
std::cout << *rit << " ";
}
return 0;
}
arr
指的是空向量,此代码将严重失败!
不定期副业成功案例分享
--i
将导致一个大问题......在进入do - while
循环之前检查(my_vector.begin() != my_vector.end())
是有意义的。do-while
循环而不是while
循环?那么你不需要对空向量进行任何特殊检查。auto
以获得更好的可读性?