Namespaces
Variants

Range-based for loop (since C++11)

From cppreference.net
C++ language
General topics
Flow control
Conditional execution statements
Iteration statements (loops)
for
range- for (C++11)
Jump statements
Functions
Function declaration
Lambda function expression
inline specifier
Dynamic exception specifications ( until C++17* )
noexcept specifier (C++11)
Exceptions
Namespaces
Types
Specifiers
constexpr (C++11)
consteval (C++20)
constinit (C++20)
Storage duration specifiers
Initialization
Expressions
Alternative representations
Literals
Boolean - Integer - Floating-point
Character - String - nullptr (C++11)
User-defined (C++11)
Utilities
Attributes (C++11)
Types
typedef declaration
Type alias declaration (C++11)
Casts
Memory allocation
Classes
Class-specific function properties
Special member functions
Templates
Miscellaneous

Exécute une boucle for sur une plage.

Utilisé comme un équivalent plus lisible à la boucle for traditionnelle opérant sur une plage de valeurs, comme tous les éléments d'un conteneur.

Table des matières

Syntaxe

attr  (optionnel) for ( init-statement  (optionnel) item-declaration : range-initializer ) statement
attr - tout nombre d' attributs
init-statement - (depuis C++20) l'un des éléments suivants
(depuis C++23)

Notez que toute init-statement doit se terminer par un point-virgule. C'est pourquoi elle est souvent décrite informellement comme une expression ou une déclaration suivie d'un point-virgule.

item-declaration - une déclaration pour chaque élément de la plage
range-initializer - une expression ou une liste d'initialisation entre accolades
statement - toute instruction (généralement une instruction composée)

Explication

La syntaxe ci-dessus produit un code équivalent à ce qui suit sauf pour l'extension de durée de vie des temporaires de range-initializer (voir ci-dessous ) (depuis C++23) (les variables et expressions encapsulées dans /* */ sont uniquement à titre d'exposition) :

{

auto&& /* range */ = initialiseur-de-plage  ;
for (auto /* begin */ = /* begin-expr */ , /* end */ = /* end-expr */ ;
/* begin */ != /* end */ ; ++ /* begin */ )
{
déclaration-élément = * /* begin */ ;
instruction
}

}

(jusqu'en C++17)

{

auto&& /* range */ = initialiseur-de-plage  ;
auto /* begin */ = /* begin-expr */ ;
auto /* end */ = /* end-expr */ ;
for ( ; /* begin */ != /* end */ ; ++ /* begin */ )
{
déclaration-élément = * /* begin */ ;
instruction
}

}

(depuis C++17)
(jusqu'en C++20)

{

instruction-init
auto&& /* range */ = initialiseur-de-plage  ;
auto /* begin */ = /* begin-expr */ ;
auto /* end */ = /* end-expr */ ;
for ( ; /* begin */ != /* end */ ; ++ /* begin */ )
{
déclaration-élément = * /* begin */ ;
instruction
}

}

(depuis C++20)

range-initializer est évalué pour initialiser la séquence ou la plage à itérer. Chaque élément de la séquence, à son tour, est déréférencé et est utilisé pour initialiser la variable avec le type et le nom donnés dans item-declaration .

item-declaration peut être l'un des éléments suivants :

Les expressions d'exposition uniquement /* begin-expr */ et /* end-expr */ sont définies comme suit :

  • Si le type de /* range */ est une référence à un type tableau R :
  • Si R est de taille N , /* begin-expr */ est /* range */ et /* end-expr */ est /* range */ + N .
  • Si R est un tableau de taille inconnue ou un tableau de type incomplet, le programme est mal formé.
  • Si le type de /* range */ est une référence à un type de classe C , et que les recherches dans la portée de C pour les noms « begin » et « end » trouvent chacune au moins une déclaration, alors /* begin-expr */ est /* range */ . begin ( ) et /* end-expr */ est /* range */ . end ( ) .
  • Sinon, /* begin-expr */ est begin ( /* range */ ) et /* end-expr */ est end ( /* range */ ) , où « begin » et « end » sont trouvés via la recherche dépendante des arguments (la recherche non-ADL n'est pas effectuée).

Si la boucle doit être interrompue au sein de statement , une break statement peut être utilisée comme instruction de terminaison.

Si l'itération actuelle doit être interrompue au sein de statement , une continue statement peut être utilisée comme raccourci.

Si un nom introduit dans init-statement est redéclaré dans le bloc le plus externe de statement , le programme est incorrect :

for (int i : {1, 2, 3})
    int i = 1; // erreur : redéclaration

Initialiseur de plage temporaire

Si range-initializer retourne un temporaire, sa durée de vie est étendue jusqu'à la fin de la boucle, comme indiqué par la liaison à la référence de transfert /* range */ .

Les durées de vie de tous les temporaires dans range-initializer ne sont pas prolongées à moins qu'ils ne soient autrement détruits à la fin du range-initializer (depuis C++23) .

// si foo() retourne par valeur
for (auto& x : foo().items()) { /* ... */ } // jusqu'à C++23 comportement indéfini

Ce problème peut être contourné en utilisant init-statement :

for (T thing = foo(); auto& x : thing.items()) { /* ... */ } // OK
(depuis C++20)


Notez que même en C++23, les paramètres non-référence des appels de fonction intermédiaires ne bénéficient pas d'une extension de durée de vie (car dans certaines ABI, ils sont détruits dans la fonction appelée, pas dans l'appelant), mais ceci n'est un problème que pour les fonctions qui sont de toute façon boguées :

using T = std::list<int>;
const T& f1(const T& t) { return t; }
const T& f2(T t)        { return t; } // always returns a dangling reference
T g();
void foo()
{
    for (auto e : f1(g())) {} // OK: lifetime of return value of g() extended
    for (auto e : f2(g())) {} // UB: lifetime of f2's value parameter ends early
}
(depuis C++23)

Notes

Si le initialisateur-de-plage est une liste d'initialisation entre accolades , /* range */ est déduit comme étant une référence à un std::initializer_list .

Il est sûr, et en fait préférable dans le code générique, d'utiliser la déduction pour les références de transfert, for ( auto && var : sequence ) .

L'interprétation par membre est utilisée si le type de la plage possède un membre nommé « begin » et un membre nommé « end ». Ceci est effectué indépendamment du fait que le membre soit un type, un membre de données, une fonction ou un énumérateur, et indépendamment de son accessibilité. Ainsi, une classe comme class meow { enum { begin = 1 , end = 2 } ; /* reste de la classe */ } ; ne peut pas être utilisée avec la boucle for basée sur les plages, même si les fonctions « begin »/« end » dans la portée de l'espace de noms sont présentes.

Bien que la variable déclarée dans la item-declaration soit généralement utilisée dans la statement , cela n'est pas obligatoire.

Depuis C++17, les types de /* begin-expr */ et de /* end-expr */ n'ont pas besoin d'être identiques, et en fait le type de /* end-expr */ n'a pas besoin d'être un itérateur : il doit seulement pouvoir être comparé par inégalité avec un. Cela permet de délimiter une plage par un prédicat (par exemple, "l'itérateur pointe vers un caractère nul").

(depuis C++17)

Lorsqu'il est utilisé avec un objet (non-const) qui a une sémantique de copie à l'écriture, la boucle for basée sur une plage peut déclencher une copie profonde en appelant (implicitement) la fonction membre non-const begin() .

Si cela est indésirable (par exemple parce que la boucle ne modifie pas réellement l'objet), cela peut être évité en utilisant std::as_const :

struct cow_string { /* ... */ }; // a copy-on-write string
cow_string str = /* ... */;
// for (auto x : str) { /* ... */ } // may cause deep copy
for (auto x : std::as_const(str)) { /* ... */ }
(depuis C++17)
Macro de test de fonctionnalité Valeur Std Fonctionnalité
__cpp_range_based_for 200907L (C++11) Boucle for basée sur des plages
201603L (C++17) Boucle for basée sur des plages avec types begin / end différents
202211L (C++23) Extension de durée de vie pour tous les objets temporaires dans l'initialiseur de plage

Mots-clés

for

Exemple

#include <iostream>
#include <vector>
int main()
{
    std::vector<int> v = {0, 1, 2, 3, 4, 5};
    for (const int& i : v) // accès par référence constante
        std::cout << i << ' ';
    std::cout << '\n';
    for (auto i : v) // accès par valeur, le type de i est int
        std::cout << i << ' ';
    std::cout << '\n';
    for (auto&& i : v) // accès par référence de transfert, le type de i est int&
        std::cout << i << ' ';
    std::cout << '\n';
    const auto& cv = v;
    for (auto&& i : cv) // accès par référence de transfert, le type de i est const int&
        std::cout << i << ' ';
    std::cout << '\n';
    for (int n : {0, 1, 2, 3, 4, 5}) // l'initialiseur peut être une
                                     // liste d'initialisation entre accolades
        std::cout << n << ' ';
    std::cout << '\n';
    int a[] = {0, 1, 2, 3, 4, 5};
    for (int n : a) // l'initialiseur peut être un tableau
        std::cout << n << ' ';
    std::cout << '\n';
    for ([[maybe_unused]] int n : a)  
        std::cout << 1 << ' '; // la variable de boucle n'a pas besoin d'être utilisée
    std::cout << '\n';
    for (auto n = v.size(); auto i : v) // l'instruction d'initialisation (C++20)
        std::cout << --n + i << ' ';
    std::cout << '\n';
    for (typedef decltype(v)::value_type elem_t; elem_t i : v)
    // déclaration typedef comme instruction d'initialisation (C++20)
        std::cout << i << ' ';
    std::cout << '\n';
    for (using elem_t = decltype(v)::value_type; elem_t i : v)
    // déclaration d'alias comme instruction d'initialisation (C++23)
        std::cout << i << ' ';
    std::cout << '\n';
}

Sortie :

0 1 2 3 4 5 
0 1 2 3 4 5 
0 1 2 3 4 5 
0 1 2 3 4 5 
0 1 2 3 4 5 
0 1 2 3 4 5 
1 1 1 1 1 1 
5 5 5 5 5 5 
0 1 2 3 4 5 
0 1 2 3 4 5

Rapports de défauts

Les rapports de défauts modifiant le comportement suivants ont été appliqués rétroactivement aux normes C++ précédemment publiées.

DR S'applique à Comportement publié Comportement corrigé
CWG 1442 C++11 il n'était pas spécifié si la recherche des non-membres
" begin " et " end " incluait la recherche non qualifiée usuelle
pas de recherche non qualifiée usuelle
CWG 2220 C++11 les noms introduits dans init-statement pouvaient être redéclarés le programme est mal formé dans ce cas
CWG 2825 C++11 si range-initializer est une liste d'initialisation entre accolades,
les non-membres " begin " et " end " seront recherchés
recherchera les membres " begin "
et " end " dans ce cas
P0962R1 C++11 l'interprétation membre était utilisée si l'un ou l'autre
des membres " begin " et " end " était présent
utilisé uniquement si les deux sont présents

Voir aussi

applique un function object unaire aux éléments d'un range
(modèle de fonction)