从头到尾迭代 C + + 向量

有没有可能从头到尾迭代一个向量?

for (vector<my_class>::iterator i = my_vector.end();
i != my_vector.begin(); /* ?! */ ) {
}

或者只有这样才有可能:

for (int i = my_vector.size() - 1; i >= 0; --i) {
}
180364 次浏览

用户 rend() / rbegin()迭代器:

for (vector<myclass>::reverse_iterator it = myvector.rbegin(); it != myvector.rend(); it++)

使用反向迭代器并从 rbegin()循环到 rend()

一种方法是:

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还在起步阶段。)

通过封闭开放范围进行反向迭代的完善的“模式”如下所示

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

如果你有 C + + 11,你可以利用 auto

for (auto it = my_vector.rbegin(); it != my_vector.rend(); ++it)
{
}

用这个密码

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

由于我不想引入类似外星人的新 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;
}

下面是一个超级简单的实现,它允许使用 for 每个构造,并且只依赖于 C + + 14 std 库:

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

这适用于提供 rstart ()和 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))
;

从 c + + 20开始,您可以使用 std::ranges::reverse_view和基于范围的 for-loop:

#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月) ,没有一个主要的编译器实现了 range 库,但是你可以求助于 Eric Niebler’s Range-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;
}

如果您可以使用 Boost 库,那么有一个 加速,射程提供 reverse范围适配器,它包括:

#include <boost/range/adaptor/reversed.hpp>

然后,结合 C + + 11的范围-for循环,你可以写下面的内容:

for (auto& elem: boost::adaptors::reverse(my_vector)) {
// ...
}

因为这段代码比使用迭代器对的代码更简短,所以可读性更好,也更不容易出错,因为需要注意的细节更少。