Menu

Boucle for basée sur un intervalle en C++ : syntaxe, auto et références

La boucle for basée sur un intervalle en C++ expliquée : itération propre sur les tableaux, vectors, strings et maps, pourquoi utiliser auto& et const auto&, et les pièges de copie et d'invalidation d'itérateurs à éviter.

Cette page contient des éditeurs exécutables - modifiez, exécutez et voyez la sortie instantanément.

« Pour chaque élément, fais ceci »

La boucle for à compteur classique est parfaite quand vous avez un indice à piloter. Mais la plupart du temps, l'indice ne vous intéresse pas vraiment : vous voulez juste toucher chaque élément d'un conteneur. Écrire for (int i = 0; i < v.size(); i++) pour cela est bruyant, et i n'est qu'à une erreur d'écart d'un dépassement de bornes.

C++11 a ajouté la boucle for basée sur un intervalle précisément pour cela. Vous nommez une variable, pointez vers un conteneur, et la boucle parcourt chaque élément à votre place :

Pas d'indice, pas de .size(), pas de bornes à se tromper. Lisez-la comme « pour chaque s dans scores ». Elle fonctionne sur les tableaux bruts, std::vector, std::string, std::map et tout ce qui expose begin() et end().

Laissez auto choisir le type

Écrire explicitement le type de l'élément fonctionne, mais c'est fragile : changez le type du conteneur et toutes les boucles doivent changer aussi. Associez le for basé sur un intervalle à auto et le compilateur déduira le type de l'élément à votre place :

Il y a pourtant un coût caché ici. Le simple auto name déduit string et copie chaque élément dans name à chaque passage. Pour un int c'est gratuit ; pour un string ou une grosse struct, c'est une allocation gaspillée à chaque itération. La solution, ce sont les références, ce qui est la prochaine chose à comprendre.

Modifier sur place avec auto&

Si vous écrivez auto x, vous obtenez une copie : assigner à x change donc la copie, pas le conteneur. Attention à ce piège :

La multiplication ne fait silencieusement rien parce que n est une copie jetable. Pour réellement modifier les éléments, prenez-les par référence avec auto& :

Le simple & fait toute la différence entre « regarder sans toucher » et « modifier sur place ». Si vous vous demandez un jour pourquoi vos modifications disparaissent, c'est presque toujours la raison.

Lire sans copier : const auto&

Quand vous devez seulement lire des éléments mais qu'ils sont coûteux à copier, utilisez const auto&. La référence évite la copie, et const documente (et impose) que vous ne modifierez rien :

Une bonne règle empirique :

for (auto x : c)         // copie    - types peu coûteux (int, char, pointeurs)
for (auto& x : c)        // modifier - vous voulez changer les éléments
for (const auto& x : c)  // lire     - types lourds que vous inspectez seulement

Par défaut, utilisez const auto& pour lire et auto& pour écrire. Ne recourez au simple auto que pour des types vraiment petits et peu coûteux à copier.

Parcourir les maps et les pairs

Un for basé sur un intervalle sur un std::map vous fournit un std::pair pour chaque entrée, avec .first (la clé) et .second (la valeur). Depuis C++17, les structured bindings permettent de décomposer ce pair en deux variables nommées directement dans l'en-tête de la boucle :

[name, age] est bien plus clair que répéter entry.first et entry.second partout. Gardez aussi const auto& ici : la clé d'une entrée de map est un string, donc copier chaque pair serait du gaspillage.

Le piège : ne redimensionnez pas pendant la boucle

Le plus gros piège est de changer la taille du conteneur pendant qu'un for basé sur un intervalle le parcourt. Appeler push_back, erase, insert ou clear peut réallouer le stockage sous-jacent et invalider les itérateurs internes de la boucle : le résultat est un comportement indéfini, c'est-à-dire des plantages ou des données corrompues, pas une erreur conviviale :

vector<int> v = {1, 2, 3};
for (int x : v) {
    v.push_back(x);   // COMPORTEMENT INDÉFINI - la réallocation invalide l'intervalle
}

Si vous devez ajouter ou supprimer des éléments pendant le traitement, passez à une boucle for basée sur un indice ou un itérateur et gérez les bornes vous-même, ou construisez un conteneur de résultats séparé et échangez-le ensuite. Deux pièges plus petits de la même famille : ne liez jamais un for basé sur un intervalle à un temporaire qui meurt immédiatement (for (auto x : makeVector()) est correct, mais for (auto& x : someObj.getTempVector()) peut pendre), et rappelez-vous que for (auto& c : myString) vous permet de modifier les caractères individuels sur place.

Suite : les fonctions

La boucle for basée sur un intervalle range l'itération, et les choix auto / auto& / const auto& que vous venez d'apprendre se transposent directement à l'un des outils les plus importants de C++. Ensuite, nous emballerons la logique dans des fonctions réutilisables : en donnant au code un nom, des paramètres et une valeur de retour, afin de pouvoir l'appeler de n'importe où au lieu de vous répéter.

Questions fréquentes

Qu'est-ce qu'une boucle for basée sur un intervalle en C++ ?

Une boucle for basée sur un intervalle parcourt chaque élément d'un conteneur (tableau, vector, string, map, etc.) sans que vous ayez à gérer un indice ou un itérateur. La syntaxe est for (auto x : container) { ... }. Elle a été ajoutée en C++11 et c'est la façon la plus propre de dire « fais ceci pour chaque élément ».

Quand dois-je utiliser auto& plutôt que auto dans une boucle for basée sur un intervalle ?

Utilisez auto& x lorsque vous voulez modifier les éléments sur place, et const auto& x lorsque vous les lisez seulement mais souhaitez éviter la copie (important pour string, vector ou les gros objets). Le simple auto x fait une copie à chaque itération : très bien pour les types peu coûteux comme int, mais du gaspillage sinon.

Peut-on changer la taille d'un vector à l'intérieur d'une boucle for basée sur un intervalle en C++ ?

Non. Appeler push_back, erase, insert ou clear sur le conteneur que vous parcourez invalide les itérateurs internes de la boucle et constitue un comportement indéfini : cela peut planter ou corrompre les données silencieusement. Si vous devez ajouter ou supprimer des éléments pendant la boucle, utilisez plutôt une boucle for basée sur un indice ou un itérateur.

Coddy programming languages illustration

Apprendre à coder avec Coddy

COMMENCER