132 votes

Itération du vecteur C++ de la fin au début

Est-il possible d'itérer un vecteur de la fin au début ?

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

Ou est-ce seulement possible avec quelque chose comme ça :

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

3 votes

En C++11, vous pouvez utiliser un for-loop basé sur l'intervalle avec un adaptateur inverse, voir ici

1 votes

Théoriquement, sur une machine 32 bits, pour la deuxième solution, si la taille du vecteur est supérieure à 2 147 483 647 + 1, il y aura un débordement (vector::size() est non signé), mais actuellement, il y a de fortes chances que vous n'atteigniez jamais cette limite (la limite actuelle des vecteurs sur les machines 32 bits est également de 1 073 741 823).

2 votes

Le problème de débordement de @StefanRogin devient réel lorsqu'au lieu de "int i" dans la boucle for, quelqu'un utilise size_t (ou peut-être auto) dans sa quête pour éviter les avertissements du compilateur (en raison de l'affectation de size() à int). Avec cela, et pour un vecteur à un seul élément, la deuxième itération déborde auto i et la boucle s'exécute avec le "i" débordé, ce qui entraîne toutes sortes de plantages.

193voto

James Curran Points 55356

Une façon de le faire :

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

rbegin() / rend() ont été spécialement conçus à cet effet. (Et oui, l'incrémentation d'un reverse_interator le déplace vers l'arrière).

Maintenant, en théorie, votre méthode (utilisant begin() / end() & --i ) fonctionnerait, std::vector L'itérateur de l'utilisateur est bidirectionnel, mais souvenez-vous, end() n'est pas le dernier élément - c'est un élément de plus que le dernier élément, donc vous devez décrémenter d'abord, et vous avez terminé quand vous atteignez begin() - mais vous devez toujours faire votre traitement.

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

} 

UPDATE : J'ai apparemment été trop agressif dans la réécriture de la for() en une boucle while() boucle. (L'important, c'est que le --i est au début).

81voto

Akavall Points 7357

Si vous disposez de C++11, vous pouvez faire usage de auto .

for (auto it = my_vector.rbegin(); it != my_vector.rend(); ++it)
{
}

36voto

AndreyT Points 139512

Le "schéma" bien établi de l'itération inverse dans les plages fermées-ouvertes se présente comme suit

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

ou, si vous préférez,

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

Ce modèle est utile, par exemple, pour l'indexation inverse d'un tableau à l'aide d'un index non signé.

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

(Les personnes qui ne sont pas familières avec ce modèle insistent souvent pour utiliser signé les types entiers pour l'indexation des tableaux, spécifiquement parce qu'ils pensent à tort que les types non signés sont en quelque sorte "inutilisables" pour l'indexation inverse)

Il peut être utilisé pour itérer sur un tableau en utilisant une technique de "pointeur glissant".

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

ou il peut être utilisé pour une itération inverse sur un vecteur en utilisant un itérateur ordinaire (non inverse)

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

33voto

florestan Points 551

À partir de c++20, vous pouvez utiliser une fonction std::ranges::reverse_view et un for-loop basé sur la plage :

#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 << ",";
}

Ou même

for(auto& i :  vec | views::reverse)

Malheureusement, au moment de la rédaction de ce document (janvier 2020), aucun compilateur majeur n'implémente la bibliothèque ranges, mais vous pouvez avoir recours à Les gammes-v3 d'Eric Niebler :

#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;
}

12voto

a1ex07 Points 23965

Utilisateur rend() / rbegin() itérateurs :

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

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X