迭代C++向量从结尾到开始

use*_*ser 78 c++ iterator vector

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

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

或者只有这样的东西才有可能:

for (int i = my_vector.size() - 1; i >= 0; --i) {
}
Run Code Online (Sandbox Code Playgroud)

Jam*_*ran 130

那么,最好的方法是:

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

rbegin()/ rend()特别为此目的而设计.(是的,递增reverse_interator会向后移动)

现在,从理论上讲,你的方法(使用begin/end&rbegin())可以工作,vector的迭代器是双向的,但是请记住,end()不是最后一个元素 - 它是超出最后一个元素的一个,所以你必须减少首先,当你到达begin()时就完成了 - 但是你仍然需要进行处理.

vector<my_class>::iterator i = my_vector.end();
while (i != my_vector.begin())
{
     --i;
    /*do stuff */

} 
Run Code Online (Sandbox Code Playgroud)

更新:我显然过于积极地将for()循环重写为while循环.(重要的部分是rend()开头的.)

  • 您能否更新答案以使用“auto”以获得更好的可读性? (2认同)

Aka*_*all 43

如果有auto,你可以利用auto.

for (auto it = my_vector.rbegin(); it != my_vector.rend(); ++it)
{
}
Run Code Online (Sandbox Code Playgroud)


flo*_*tan 27

从 c++20 开始,您可以使用 astd::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 << ",";
}
Run Code Online (Sandbox Code Playgroud)

甚至

for(auto& i :  vec | views::reverse)
Run Code Online (Sandbox Code Playgroud)

不幸的是,在撰写本文时(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;
}
Run Code Online (Sandbox Code Playgroud)

  • @DinoSaric 这是 C++20 中的一项新功能,允许在范围上进行组合操作。例如,请参阅本教程:https://hannes.hauswedell.net/post/2019/11/30/range_intro/ (3认同)
  • 我对这行“for(auto&amp; i : vec |views::reverse)”感到困惑。它是如何工作的?“|”在这里做什么? (2认同)

AnT*_*AnT 24

通过闭合开放范围反向迭代的完善的"模式"如下所示

// Iterate over [begin, end) range in reverse
for (iterator = end; iterator-- != begin; ) {
  // Process `*iterator`
}
Run Code Online (Sandbox Code Playgroud)

或者,如果您愿意,

// Iterate over [begin, end) range in reverse
for (iterator = end; iterator != begin; ) {
  --iterator;
  // Process `*iterator`
}
Run Code Online (Sandbox Code Playgroud)

例如,该模式可用于使用无符号索引对数组进行反向索引

int array[N];
...
// Iterate over [0, N) range in reverse
for (unsigned i = N; i-- != 0; ) {
  array[i]; // <- process it
}
Run Code Online (Sandbox Code Playgroud)

(不熟悉此模式的人经常坚持使用有符号整数类型进行数组索引,因为他们错误地认为无符号类型会阻止反向索引)

它可以用于使用"滑动指针"技术迭代数组

// Iterate over [array, array + N) range in reverse
for (int *p = array + N; p-- != array; ) {
  *p; // <- process it
}
Run Code Online (Sandbox Code Playgroud)

或者它可以用于使用普通(非反向)迭代器对向量进行反向迭代

for (vector<my_class>::iterator i = my_vector.end(); i-- != my_vector.begin(); ) {
  *i; // <- process it
}
Run Code Online (Sandbox Code Playgroud)

  • @ThomasSchmid 这些循环从不尝试在“end()”处访问。尽管它们似乎从 `end()` 开始,但它们总是确保在第一次访问之前递减迭代器。 (2认同)
  • @科林·埃加兹!真丑!您正在测试“反向”*四*次——其中两次在循环内。当然,测试布尔值非常快,但是为什么不需要这样做呢?特别是,因为唯一的目的似乎是使代码不可读。我们使用两个单独的循环怎么样?`if(反转) 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);} ` (2认同)

a1e*_*x07 9

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

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


Ste*_*end 6

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


Yak*_*ont 5

template<class It>
std::reverse_iterator<It> reversed( It it ) {
  return std::reverse_iterator<It>(std::forward<It>(it));
}
Run Code Online (Sandbox Code Playgroud)

然后:

for( auto rit = reversed(data.end()); rit != reversed(data.begin()); ++rit ) {
  std::cout << *rit;
Run Code Online (Sandbox Code Playgroud)

或者在C++ 14中只做:

for( auto rit = std::rbegin(data); rit != std::rend(data); ++rit ) {
  std::cout << *rit;
Run Code Online (Sandbox Code Playgroud)

在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) ) };
}
Run Code Online (Sandbox Code Playgroud)

现在你可以这样做:

for (auto&& x : backwards(ctnr))
  std::cout << x;
Run Code Online (Sandbox Code Playgroud)

我认为这很漂亮.