No C ++ 11, você pode usar for-loop baseado em intervalo com adaptador reverso, veja aqui
MM
1
teoricamente, em uma máquina de 32 bits, para a segunda solução, se o tamanho do vetor for maior que 2.147.483.647 + 1, ele irá estourar (vector :: size () não tem sinal), mas atualmente as chances são de que você nunca vai atingir esse limite (também o limite do vetor atual em máquinas de 32 bits é 1.073.741.823).
Stefan Rogin
Respostas:
157
A melhor maneira é:
for(vector<my_class>::reverse_iterator i = my_vector.rbegin();
i != my_vector.rend();++i ){}
rbegin()/ rend()Foram especialmente concebidos para o efeito. (E sim, incrementar a reverse_interatormove para trás.)
Agora, em teoria, seu método (usando begin()/ end()& --i) funcionaria, std::vectoro iterador sendo bidirecional, mas lembre-se, end()não é o último elemento - é um além do último elemento, então você teria que decrementar primeiro, e você é feito quando você chegar begin()- mas você ainda terá que fazer o seu processamento.
vector<my_class>::iterator i = my_vector.end();while(i != my_vector.begin()){--i;/*do stuff */}
ATUALIZAÇÃO: Eu fui aparentemente muito agressivo ao reescrever o for()loop em um while()loop. (A parte importante é que o --iestá no início.)
Acabei de perceber que --iisso causará um grande problema se o recipiente estiver vazio ... Antes de entrar no do - whileloop, faz sentido verificar (my_vector.begin() != my_vector.end()).
a1ex07
1
Por que você está usando um do-whileloop em vez de apenas um whileloop? Então você não precisaria de nenhuma verificação especial para vetores vazios.
Jamesdlin
Você poderia atualizar a resposta a ser usada autopara melhor legibilidade?
LNJ
59
Se você tiver o C ++ 11, poderá usar auto.
for(auto it = my_vector.rbegin(); it != my_vector.rend();++it){}
O "padrão" bem estabelecido para a iteração reversa através de intervalos fechados-abertos é o seguinte
// Iterate over [begin, end) range in reversefor(iterator= end;iterator--!= begin;){// Process `*iterator`}
ou, se preferir,
// Iterate over [begin, end) range in reversefor(iterator= end;iterator!= begin;){--iterator;// Process `*iterator`}
Este padrão é útil, por exemplo, para indexação reversa de uma matriz usando um índice não assinado
intarray[N];...// Iterate over [0, N) range in reversefor(unsigned i = N; i--!=0;){array[i];// <- process it}
(Pessoas não familiarizadas com este padrão muitas vezes insistem em usar tipos inteiros assinados para indexação de array, especificamente porque acreditam incorretamente que tipos não assinados são de alguma forma "inutilizáveis" para indexação reversa)
Ele pode ser usado para iterar sobre uma matriz usando uma técnica de "ponteiro deslizante"
// Iterate over [array, array + N) range in reversefor(int*p =array+ N; p--!=array;){*p;// <- process it}
ou pode ser usado para iteração reversa sobre um vetor usando um iterador comum (não reverso)
for(vector<my_class>::iterator i = my_vector.end(); i--!= my_vector.begin();){*i;// <- process it}
cppreference.com diz, acessar o elemento em end () "resulta em comportamento indefinido", então eu acho que os loops devem começar em--end()
Thomas Schmid
@ThomasSchmid Esses loops nunca tentam acessar em end(). Mesmo que pareçam começar em end(), eles sempre se certificam de diminuir o iterador antes do primeiro acesso.
Antes de
Isso é muito melhor do que rbegin / rend porque você pode fazer o loop de outra maneira em tempo de execução (sem modelo) 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
@colin Egads! tão feio !. Você está testando reversedquatro vezes - duas delas dentro de um loop. Claro, testar um booleano é muito rápido, mas ainda assim, por que trabalhar você não precisa? Especialmente porque o único propósito parece ser tornar o código ilegível. que tal usarmos dois loops separados? 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);}
James Curran
Na verdade você não entendeu meu ponto. Você está absolutamente certo em dividi-lo em dois ifs, mas eu queria me livrar do modelo no doStuff(). Ainda é possível, porém, com os dois ifs que você tem, fazendo o loop ao contrário no primeiro.
colin
11
Começando com c ++ 20, você pode usar um std::ranges::reverse_viewe um loop for baseado em intervalo:
#include<ranges>#include<vector>#include<iostream>usingnamespace 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 <<",";}
Ou mesmo
for(auto& i : vec | views::reverse)
Infelizmente, no momento da escrita (janeiro de 2020), nenhum grande compilador implementou a biblioteca de intervalos, mas você pode recorrer a intervalos-v3 de Eric Niebler :
#include<iostream>#include<vector>#include"range/v3/all.hpp"int main(){usingnamespace 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 <<",";}return0;}
Aqui está uma implementação super simples que permite o uso de for each construct e depende apenas da biblioteca std C ++ 14:
namespaceDetails{// simple storage of a begin and end iteratortemplate<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){usingnamespace std;returnDetails::iterator_range(rbegin(collection), rend(collection));}
Isso funciona com coisas que fornecem um rbegin () e rend (), bem como com matrizes estáticas.
std::vector<int> collection{5,9,15,22};for(auto e : backwards(collection));long values[]={3,6,9,12};for(auto e : backwards(values));
Como esse código é mais breve do que aquele que usa o par de iteradores, ele pode ser mais legível e menos sujeito a erros, pois há menos detalhes aos quais prestar atenção.
//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;}
Este código falha terrivelmente, se se vecrefere a um vetor vazio!
Kai Petzke
-2
Como não quero introduzir a nova sintaxe alienígena de Marte, e simplesmente quero construir sobre primitivos existentes, os snippets abaixo parecem funcionar:
#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 <<" ";}return0;}
Respostas:
A melhor maneira é:
rbegin()
/rend()
Foram especialmente concebidos para o efeito. (E sim, incrementar areverse_interator
move para trás.)Agora, em teoria, seu método (usando
begin()
/end()
&--i
) funcionaria,std::vector
o iterador sendo bidirecional, mas lembre-se,end()
não é o último elemento - é um além do último elemento, então você teria que decrementar primeiro, e você é feito quando você chegarbegin()
- mas você ainda terá que fazer o seu processamento.ATUALIZAÇÃO: Eu fui aparentemente muito agressivo ao reescrever o
for()
loop em umwhile()
loop. (A parte importante é que o--i
está no início.)fonte
--i
isso causará um grande problema se o recipiente estiver vazio ... Antes de entrar nodo - while
loop, faz sentido verificar(my_vector.begin() != my_vector.end())
.do-while
loop em vez de apenas umwhile
loop? Então você não precisaria de nenhuma verificação especial para vetores vazios.auto
para melhor legibilidade?Se você tiver o C ++ 11, poderá usar
auto
.fonte
O "padrão" bem estabelecido para a iteração reversa através de intervalos fechados-abertos é o seguinte
ou, se preferir,
Este padrão é útil, por exemplo, para indexação reversa de uma matriz usando um índice não assinado
(Pessoas não familiarizadas com este padrão muitas vezes insistem em usar tipos inteiros assinados para indexação de array, especificamente porque acreditam incorretamente que tipos não assinados são de alguma forma "inutilizáveis" para indexação reversa)
Ele pode ser usado para iterar sobre uma matriz usando uma técnica de "ponteiro deslizante"
ou pode ser usado para iteração reversa sobre um vetor usando um iterador comum (não reverso)
fonte
--end()
end()
. Mesmo que pareçam começar emend()
, eles sempre se certificam de diminuir o iterador antes do primeiro acesso.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
quatro vezes - duas delas dentro de um loop. Claro, testar um booleano é muito rápido, mas ainda assim, por que trabalhar você não precisa? Especialmente porque o único propósito parece ser tornar o código ilegível. que tal usarmos dois loops separados?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
s, mas eu queria me livrar do modelo nodoStuff()
. Ainda é possível, porém, com os doisif
s que você tem, fazendo o loop ao contrário no primeiro.Começando com c ++ 20, você pode usar um
std::ranges::reverse_view
e um loop for baseado em intervalo:Ou mesmo
Infelizmente, no momento da escrita (janeiro de 2020), nenhum grande compilador implementou a biblioteca de intervalos, mas você pode recorrer a intervalos-v3 de Eric Niebler :
fonte
rend() / rbegin()
Iteradores de usuário :for (vector<myclass>::reverse_iterator it = myvector.rbegin(); it != myvector.rend(); it++)
fonte
Então:
Como alternativa, em C ++ 14, basta fazer:
Em C ++ 03/11 a maioria dos recipientes convencionais têm uma
.rbegin()
e.rend()
método bem.Finalmente, você pode escrever o adaptador de alcance da
backwards
seguinte maneira:e agora você pode fazer isso:
o que eu acho muito bonito.
fonte
Use iteradores reversos e loop de
rbegin()
pararend()
fonte
Aqui está uma implementação super simples que permite o uso de for each construct e depende apenas da biblioteca std C ++ 14:
Isso funciona com coisas que fornecem um rbegin () e rend (), bem como com matrizes estáticas.
fonte
Se você pode usar a Biblioteca Boost, há o Boost.Range que fornece o
reverse
adaptador de intervalo incluindo:Então, em combinação com um loop de intervalo do C ++ 11
for
, você pode simplesmente escrever o seguinte:Como esse código é mais breve do que aquele que usa o par de iteradores, ele pode ser mais legível e menos sujeito a erros, pois há menos detalhes aos quais prestar atenção.
fonte
boost::adaptors::reverse
é muito útil!Eu gosto do iterador reverso no final de Yakk - a resposta de Adam Nevraumont, mas parecia complicado para o que eu precisava, então escrevi isto:
Sou capaz de usar um iterador normal como este:
e altere-o para iterar ao contrário:
fonte
use este código
fonte
vec
refere a um vetor vazio!Como não quero introduzir a nova sintaxe alienígena de Marte, e simplesmente quero construir sobre primitivos existentes, os snippets abaixo parecem funcionar:
fonte
arr
refere a um vetor vazio!