从头到尾迭代C ++向量


96

是否可以从头到尾迭代向量?

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

还是只有这样才能做到:

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

2
在C ++ 11可以使用范围为基础的循环使用反向适配器,在这里看到的
MM

1
从理论上讲,在32位计算机上,对于第二个解决方案,如果向量大小大于2,147,483,647 + 1,它将溢出(vector :: size()是无符号的),但是目前很有可能您永远都不会达到该限制(也32位计算机上的当前向量限制为1,073,741,823)。
Stefan Rogin 2014年

Answers:


156

最好的方法是:

for (vector<my_class>::reverse_iterator i = my_vector.rbegin(); 
        i != my_vector.rend(); ++i ) { 
} 

rbegin()/ rend()是专门为此目的而设计的。(是的,增加a 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开头。)


我刚刚意识到,--i如果容器为空,这将带来一个大问题。进入do - while循环之前,检查是有意义的(my_vector.begin() != my_vector.end())
2010年

1
为什么要使用do-while循环而不只是while循环?这样,您就不需要对空向量进行任何特殊检查。
jamesdlin

您可以更新答案以auto提高可读性吗?
LNJ

57

如果您拥有C ++ 11,则可以使用auto

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

28

建立良好的“模式”,用于在封闭的开放范围内进行反向迭代

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

cppreference.com说,访问end()的元素“导致未定义的行为”,所以我认为,循环应始于--end()
Thomas Schmid,

@ThomasSchmid这些循环永远不会尝试访问end()。即使它们似乎始于end(),它们也始终确保在首次访问之前减小迭代器。
AnT

这比rbegin / rend好得多,因为您可以在运行时以其他方式循环(无模板) 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; }
colin 18/12/18

@colin阿加兹!真丑!您正在测试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您可以通过在第一个循环中以相反的方式循环使用两个,但仍然可以实现。
科林

11

从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月),没有主要的编译器实现ranges库,但是您可以诉诸Eric Niebler的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;
}

9

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

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


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

我认为这很漂亮。



1

这是一个超简单的实现,允许对每个结构使用,并且仅依赖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));
}

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

0

我喜欢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
}

0

如果可以使用Boost库,则可以通过以下方式使用Boost.Range提供reverse范围适配器

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

然后,结合C ++ 11的range- forloop,您可以编写以下代码:

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

由于此代码比使用迭代器对的代码要简短,因此,由于要注意的细节较少,因此它更易读,并且不易出错。


-1

使用此代码

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

-2

由于我不想引入外星人火星的新语法,而我只是想在现有的基元上构建,因此以下代码片段似乎有效:

#include <vector>
#include <iostream>

int main (int argc,char *argv[])
{
    std::vector<int> arr{1,2,3,4,5};
    std::vector<int>::iterator it;

    for (it = arr.begin(); it != arr.end(); it++) {
        std::cout << *it << " ";
    }

    std::cout << "\n************\n";

    for (it = arr.end() - 1; it != arr.begin()-1;it--) {
        std::cout << *it << " ";
    }

    return 0;
}
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.