Итерация вектора C ++ от конца до начала - PullRequest
65 голосов
/ 31 августа 2010

Возможно ли перебрать вектор от конца к началу?

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

Или это возможно только с чем-то подобным:

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

Ответы [ 10 ]

117 голосов
/ 31 августа 2010

Ну, лучший способ это:

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

rbegin () / rend () специально разработан для этой цели. (И да, приращение реверс-интегратора перемещает его назад)

Теперь, теоретически, ваш метод (с использованием begin / end & --i) будет работать, итератор вектора будет двунаправленным, но помните, что end () не последний элемент - это один за последним элементом, сначала нужно уменьшить значение, и вы закончите, когда достигнете begin () - но вам все равно придется выполнять обработку.

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

} 

ОБНОВЛЕНИЕ: Я явно слишком агрессивно переписывал цикл for () в цикл while. (Важной частью является то, что --i находится в начале.)

35 голосов
/ 20 июля 2014

Если у вас есть C ++ 11, вы можете использовать auto.

for (auto it = my_vector.rbegin(); it != my_vector.rend(); ++it)
{
}
20 голосов
/ 31 августа 2010

Хорошо зарекомендовавший себя «шаблон» для обратной итерации по закрытым открытым диапазонам выглядит следующим образом

// 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
}
9 голосов
/ 31 августа 2010

Пользователь rend() / rbegin() итераторы:

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

5 голосов
/ 27 января 2015
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;

что я считаю довольно симпатичным.

4 голосов
/ 31 августа 2010

Используйте обратные итераторы и цикл от rbegin() до rend()

0 голосов
/ 30 апреля 2019

Вот очень простая реализация, которая позволяет использовать для каждой конструкции и опирается только на библиотеку 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 голосов
/ 20 апреля 2019

Поскольку я не хочу вводить инопланетный Марс с новым синтаксисом, а просто хочу использовать существующие примитивы, приведенные ниже фрагменты работают:

#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;
}
0 голосов
/ 26 января 2019

Мне нравится обратный итератор в конце Yakk - ответ Адама Невраумона, но он показался мне сложным, поэтому я написал это:

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 голосов
/ 22 июля 2014

используйте этот код

//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;
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...