Question

Quelle est la bonne façon de parcourir un vecteur en C ++?

Considérons ces deux fragments de code, celui-ci fonctionne bien:

for (unsigned i=0; i < polygon.size(); i++) {
    sum += polygon[i];
}

et celui-ci:

for (int i=0; i < polygon.size(); i++) {
    sum += polygon[i];
}

qui génère warning: comparison between signed and unsigned integer expressions.

Comme je suis nouveau dans le monde du C ++, la variable unsigned me semble un peu effrayante et je sais que <=> les variables peuvent être dangereuses si elles ne sont pas utilisées correctement, donc - est-ce correct?

Était-ce utile?

La solution

Itérer en arrière

Voir cette réponse .

Itérations en avant

Ceci est presque identique. Il suffit de changer les itérateurs / swap décrément par incrément. Vous devriez préférer les itérateurs. Certaines personnes vous disent d’utiliser std::size_t comme type de variable d’index. Cependant, ce n'est pas portable. Utilisez toujours le type size_type typedef du conteneur (bien que vous puissiez vous échapper uniquement avec une conversion dans le cas d'itération en aval, il pourrait en réalité se tromper dans le cas d'itération en arrière lors de l'utilisation de sizeof, dans le cas où <=> est plus large que ce qui est le typedef de <=>):

Utilisation de std :: vector

Utilisation des itérateurs

for(std::vector<T>::iterator it = v.begin(); it != v.end(); ++it) {
    /* std::cout << *it; ... */
}

Il est important de toujours utiliser le formulaire d'incrément de préfixe pour les itérateurs dont vous ne connaissez pas les définitions. Cela garantira que votre code est aussi générique que possible.

Utilisation de la plage C ++ 11

for(auto const& value: a) {
     /* std::cout << value; ... */

Utilisation des index

for(std::vector<int>::size_type i = 0; i != v.size(); i++) {
    /* std::cout << v[i]; ... */
}

Utilisation de tableaux

Utilisation des itérateurs

for(element_type* it = a; it != (a + (sizeof a / sizeof *a)); it++) {
    /* std::cout << *it; ... */
}

Utilisation de la plage C ++ 11

for(std::size_t i = 0; i != (sizeof a / sizeof *a); i++) {
    /* std::cout << a[i]; ... */
}

Utilisation des index

<*>

Lisez la réponse itérative en arrière à quel problème l'approche <=> peut céder, cependant.

Autres conseils

Quatre ans ont passé, Google m'a donné cette réponse. Avec le standard C ++ 11 (aussi appelé C ++ 0x ) il existe en fait une nouvelle manière agréable de procéder (au prix de la compatibilité en amont): le nouveau mot clé auto. Cela vous évite d'avoir à spécifier explicitement le type d'itérateur à utiliser (en répétant le type de vecteur), lorsqu'il est évident (pour le compilateur), quel type d'utiliser. Avec v comme votre vector, vous pouvez faire quelque chose comme ceci:

for ( auto i = v.begin(); i != v.end(); i++ ) {
    std::cout << *i << std::endl;
}

C ++ 11 va encore plus loin et vous donne une syntaxe spéciale pour parcourir des collections telles que des vecteurs. Cela supprime la nécessité d'écrire des choses toujours identiques:

for ( auto &i : v ) {
    std::cout << i << std::endl;
}

Pour le voir dans un programme de travail, créez un fichier auto.cpp:

#include <vector>
#include <iostream>

int main(void) {
    std::vector<int> v = std::vector<int>();
    v.push_back(17);
    v.push_back(12);
    v.push_back(23);
    v.push_back(42);
    for ( auto &i : v ) {
        std::cout << i << std::endl;
    }
    return 0;
}

Au moment d'écrire ceci, lorsque vous compilez ceci avec g ++ , vous devez normalement le configurer pour qu'il fonctionne avec la nouvelle norme en donnant un indicateur supplémentaire:

g++ -std=c++0x -o auto auto.cpp

Vous pouvez maintenant exécuter l'exemple suivant:

$ ./auto
17
12
23
42

Veuillez noter que les instructions de compilation et d’exécution sont spécifiques au compilateur gnu c ++ sous Linux , le programme doit être une plate-forme (et un compilateur). ) indépendant.

Dans le cas spécifique de votre exemple, j'utiliserais les algorithmes STL pour accomplir cela.

#include <numeric> 

sum = std::accumulate( polygon.begin(), polygon.end(), 0 );

Pour un cas plus général, mais qui reste assez simple, je choisirais:

#include <boost/lambda/lambda.hpp>
#include <boost/lambda/bind.hpp>

using namespace boost::lambda;
std::for_each( polygon.begin(), polygon.end(), sum += _1 );

À propos de la réponse de Johannes Schaub:

for(std::vector<T*>::iterator it = v.begin(); it != v.end(); ++it) { 
...
}

Cela peut fonctionner avec certains compilateurs mais pas avec gcc. Le problème ici est la question de savoir si std :: vector :: iterator est un type, une variable (membre) ou une fonction (méthode). Nous obtenons l'erreur suivante avec gcc:

In member function ‘void MyClass<T>::myMethod()’:
error: expected `;' before ‘it’
error: ‘it’ was not declared in this scope
In member function ‘void MyClass<T>::sort() [with T = MyClass]’:
instantiated from ‘void MyClass<T>::run() [with T = MyClass]’
instantiated from here
dependent-name ‘std::vector<T*,std::allocator<T*> >::iterator’ is parsed as a non-type, but instantiation yields a type
note: say ‘typename std::vector<T*,std::allocator<T*> >::iterator’ if a type is meant

La solution utilise le mot clé 'typename' comme indiqué:

typename std::vector<T*>::iterator it = v.begin();
for( ; it != v.end(); ++it) {
...

Un appel à vector<T>::size() retourne une valeur de type std::vector<T>::size_type, pas int, unsigned int ou autre.

De manière générale, l'itération sur un conteneur en C ++ est effectuée à l'aide de itérateurs , comme ceci.

std::vector<T>::iterator i = polygon.begin();
std::vector<T>::iterator end = polygon.end();

for(; i != end; i++){
    sum += *i;
}

Où T est le type de données que vous stockez dans le vecteur.

Ou en utilisant les différents algorithmes d'itération (std::transform, std::copy, std::fill, std::for_each et autres).

Utilisez size_t:

for (size_t i=0; i < polygon.size(); i++)

Citant Wikipedia :

  

Les fichiers d'en-tête stdlib.h et stddef.h définissent un type de données appelé <=> utilisé pour représenter la taille d'un objet. Les fonctions de bibliothèque prenant des tailles s’attendent à ce qu’elles soient de type <=>, et l’opérateur sizeof est évalué à <=>.

     

Le type réel de <=> dépend de la plate-forme; Une erreur courante est de supposer que <=> est identique à unsigned int, ce qui peut entraîner des erreurs de programmation, en particulier lorsque les architectures 64 bits deviennent plus courantes.

J'utilise habituellement BOOST_FOREACH:

#include <boost/foreach.hpp>

BOOST_FOREACH( vector_type::value_type& value, v ) {
    // do something with 'value'
}

Cela fonctionne sur les conteneurs STL, les tableaux, les chaînes de style C, etc.

Un peu d'histoire:

Pour indiquer si un nombre est négatif ou non, utilisez un bit de "signature". int est un type de données signé signifiant qu'il peut contenir des valeurs positives et négatives (environ -2 milliards à 2 milliards). Unsigned ne peut stocker que des nombres positifs (et puisqu'il ne gaspille pas un peu les métadonnées, il peut en stocker davantage: 0 à environ 4 milliards de dollars).

std::vector::size() retourne un unsigned, car comment un vecteur peut-il avoir une longueur négative?

L'avertissement vous indique que l'opérande de droite de votre déclaration d'inégalité peut contenir plus de données que la gauche.

Essentiellement, si vous avez un vecteur de plus de 2 milliards d’entrées et que vous utilisez un entier pour l’indexation, vous rencontrerez des problèmes de débordement (l’int sera de nouveau négatif à 2 milliards).

Pour être complète, la syntaxe C ++ 11 n'autorise qu'une seule version pour les itérateurs ( ref ):

for(auto it=std::begin(polygon); it!=std::end(polygon); ++it) {
  // do something with *it
}

Ce qui est également confortable pour l'itération inverse

for(auto it=std::end(polygon)-1; it!=std::begin(polygon)-1; --it) {
  // do something with *it
}

En C ++ 11

J'utiliserais des algorithmes généraux tels que for_each pour éviter de rechercher le bon type d'itérateur et d'expression lambda afin d'éviter les fonctions / objets nommés supplémentaires.

Le court & "joli &"; exemple pour votre cas particulier (en supposant que polygone est un vecteur d’entiers):

for_each(polygon.begin(), polygon.end(), [&sum](int i){ sum += i; });

testé sur: http://ideone.com/i6Ethd

N'oubliez pas d ' inclure: l'algorithme et, bien sûr, le vecteur:)

Microsoft a également un bel exemple à ce sujet:
source: http://msdn.microsoft.com/en -us / library / dd293608.aspx

#include <algorithm>
#include <iostream>
#include <vector>
using namespace std;

int main() 
{
   // Create a vector object that contains 10 elements.
   vector<int> v;
   for (int i = 1; i < 10; ++i) {
      v.push_back(i);
   }

   // Count the number of even numbers in the vector by 
   // using the for_each function and a lambda.
   int evenCount = 0;
   for_each(v.begin(), v.end(), [&evenCount] (int n) {
      cout << n;
      if (n % 2 == 0) {
         cout << " is even " << endl;
         ++evenCount;
      } else {
         cout << " is odd " << endl;
      }
   });

   // Print the count of even numbers to the console.
   cout << "There are " << evenCount 
        << " even numbers in the vector." << endl;
}
for (vector<int>::iterator it = polygon.begin(); it != polygon.end(); it++)
    sum += *it; 

Le premier type est correct, et correct au sens strict. (Si vous y réfléchissez, la taille ne peut jamais être inférieure à zéro.) Cet avertissement me semble toutefois être l’un des bons candidats pour être ignoré.

Déterminez si vous devez itérer du tout

L'en-tête <algorithm> standard nous fournit des fonctionnalités pour cela:

using std::begin;  // allows argument-dependent lookup even
using std::end;    // if the container type is unknown here
auto sum = std::accumulate(begin(polygon), end(polygon), 0);

D'autres fonctions de la bibliothèque d'algorithmes effectuent des tâches courantes - assurez-vous de connaître les ressources disponibles si vous voulez économiser votre effort.

Détail obscur mais important: si vous dites & "pour (auto it) &"; comme suit, vous obtenez une copie de l'objet et non de l'élément réel:

struct Xs{int i} x;
x.i = 0;
vector <Xs> v;
v.push_back(x);
for(auto it : v)
    it.i = 1;         // doesn't change the element v[0]

Pour modifier les éléments du vecteur, vous devez définir l'itérateur comme référence:

for(auto &it : v)

Si votre compilateur le prend en charge, vous pouvez utiliser une plage basée sur pour accéder aux éléments vectoriels:

vector<float> vertices{ 1.0, 2.0, 3.0 };

for(float vertex: vertices){
    std::cout << vertex << " ";
}

Impressions: 1 2 3. Notez que vous ne pouvez pas utiliser cette technique pour modifier les éléments du vecteur.

Les deux segments de code fonctionnent de la même manière. Cependant, unsigned int & Quot; la route est correcte. L'utilisation de types int non signés fonctionnera mieux avec le vecteur de l'instance dans laquelle vous l'avez utilisé. L'appel de la fonction membre size () sur un vecteur renvoie une valeur entière non signée. Vous souhaitez donc comparer la variable & Quot; i & Quot; à une valeur de son propre type.

Aussi, si vous êtes toujours un peu inquiet de savoir comment & "unsigned int &"; regarde dans votre code, essayez & "; uint &"; Il s’agit d’une version abrégée de & Quot; unsigned int & Quot; et cela fonctionne exactement de la même manière. Vous n'avez également pas besoin d'inclure d'autres en-têtes pour l'utiliser.

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top