Можно ли перебрать вектор от конца до начала?
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>::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()
были специально разработаны для этой цели. (И да, при увеличении 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())
.
do-while
цикл, а не просто while
цикл? Тогда вам не понадобится специальная проверка на пустые векторы.
auto
для лучшей читаемости?
Хорошо зарекомендовавший себя «шаблон» для обратного перебора диапазонов закрыто-открыто выглядит следующим образом.
// 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
s, которые у вас есть, путем обратного цикла на первом.
Начиная с 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 г.) ни один крупный компилятор не реализует библиотеку диапазонов, но вы можете прибегнуть к диапазонам Эрика Ниблера-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;
}
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;
В качестве альтернативы в С ++ 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;
что я считаю довольно красивым.
Используйте обратные итераторы и rbegin()
выполните цикл от доrend()
Мне нравится обратный итератор в конце ответа Якка - Адама Неврамонта, но он казался сложным для того, что мне было нужно, поэтому я написал это:
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 ++ 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))
;
Если вы можете использовать библиотеку Boost, есть Boost.Range, который предоставляет reverse
адаптер диапазона , включая:
#include <boost/range/adaptor/reversed.hpp>
Затем в сочетании с циклом диапазона C ++ 11for
вы можете просто написать следующее:
for (auto& elem: boost::adaptors::reverse(my_vector)) {
// ...
}
Поскольку этот код короче, чем код, использующий пару итераторов, он может быть более читабельным и менее подверженным ошибкам, так как меньше деталей, на которые следует обращать внимание.
boost::adaptors::reverse
очень полезно!
используйте этот код
//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
ссылается на пустой вектор!