В C ++ 11 вы можете использовать цикл for на основе диапазона с обратным адаптером, см. Здесь
MM
1
теоретически на 32-битной машине для второго решения, если размер вектора больше 2147483647 + 1, он будет переполняться (vector :: size () беззнаковый), но в настоящее время есть вероятность, что вы никогда не достигнете этого предела (также текущий предел вектора на 32-битных машинах составляет 1 073 741 823).
Стефан Рогин
Проблема переполнения @StefanRogin становится реальной, когда вместо int i в цикле for кто-то использует size_t (или, возможно, auto) в своем поиске, чтобы избежать предупреждений компилятора (из-за присвоения size () int). При этом и для вектора с одним элементом вторая итерация переполняет auto i, и цикл выполняется с переполненным «i», что приводит к всевозможным сбоям.
n-mam
Ответы:
164
Лучший способ:
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()).
a1ex07
1
Почему вы используете do-whileцикл, а не просто whileцикл? Тогда вам не понадобится специальная проверка на пустые векторы.
jamesdlin
Не могли бы вы обновить ответ, чтобы использовать его autoдля лучшей читаемости?
LNJ
60
Если у вас есть C ++ 11, вы можете использовать auto.
for (auto it = my_vector.rbegin(); it != my_vector.rend(); ++it)
{
}
Хорошо зарекомендовавший себя «шаблон» для обратного перебора диапазонов закрыто-открыто выглядит следующим образом.
// Iterate over [begin, end) range in reversefor (iterator = end; iterator-- != begin; ) {
// Process `*iterator`
}
или, если хотите,
// Iterate over [begin, end) range in reversefor (iterator = end; iterator != begin; ) {
--iterator;
// Process `*iterator`
}
Этот шаблон полезен, например, для обратной индексации массива с использованием беззнакового индекса.
intarray[N];
...
// Iterate over [0, N) range in reversefor (unsigned i = N; i-- != 0; ) {
array[i]; // <- process it
}
(Люди , незнакомые с этой моделью часто настаивают на использование подписали целое число типов для индексации массивов именно потому , что они ошибочно полагают , что беззнаковые типы, так или иначе «непригодные» для обратной индексации)
Его можно использовать для перебора массива с помощью техники «скользящего указателя».
// Iterate over [array, array + N) range in reversefor (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
1
@ThomasSchmid Эти циклы никогда не пытаются получить доступ к end(). Даже если кажется, что они начинаются с end(), они всегда убирают итератор перед первым доступом.
AnT 05
Это намного лучше, чем 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; }
Колин
1
@colin Egads! это ужасно!. Вы тестируете 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(). Тем не менее, это выполнимо с двумя ifs, которые у вас есть, путем обратного цикла на первом.
Colin
17
Начиная с c ++ 20, вы можете использовать std::ranges::reverse_viewцикл for на основе диапазона и:
К сожалению, на момент написания (январь 2020 г.) ни один крупный компилятор не реализует библиотеку диапазонов, но вы можете прибегнуть к диапазонам Эрика Ниблера-v3 :
Вот суперпростая реализация, которая позволяет использовать для каждой конструкции и полагается только на стандартную библиотеку C ++ 14:
namespace Details {
// simple storage of a begin and end iteratortemplate<classT>
structiterator_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>
autobackwards(T & collection){
usingnamespacestd;
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))
;
for (auto& elem: boost::adaptors::reverse(my_vector)) {
// ...
}
Поскольку этот код короче, чем код, использующий пару итераторов, он может быть более читабельным и менее подверженным ошибкам, так как меньше деталей, на которые следует обращать внимание.
//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ссылается на пустой вектор!
Кай Петцке,
-2
Поскольку я не хочу вводить новый синтаксис C ++, похожий на пришельцев, и я просто хочу развить существующие примитивы, приведенные ниже фрагменты, похоже, работают:
#include<vector>#include<iostream>intmain(int argc,char *argv[]){
std::vector<int> arr{1,2,3,4,5};
std::vector<int>::iterator it;
// iterate forwardfor (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++ waystd::vector<int>::reverse_iterator rit;
for (rit = arr.rbegin(); rit != arr.rend(); rit++) {
std::cout << *rit << " ";
}
return0;
}
Ответы:
Лучший способ:
for (vector<my_class>::reverse_iterator i = my_vector.rbegin(); i != my_vector.rend(); ++i ) { }
rbegin()
/rend()
были специально разработаны для этой цели. (И да, при увеличении areverse_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
для лучшей читаемости?Если у вас есть C ++ 11, вы можете использовать
auto
.for (auto it = my_vector.rbegin(); it != my_vector.rend(); ++it) { }
источник
Хорошо зарекомендовавший себя «шаблон» для обратного перебора диапазонов закрыто-открыто выглядит следующим образом.
// 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; }
источник
Пользовательские
rend() / rbegin()
итераторы:for (vector<myclass>::reverse_iterator it = myvector.rbegin(); it != myvector.rend(); it++)
источник
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 ++ 11
for
вы можете просто написать следующее: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
ссылается на пустой вектор!