Aujourd’hui, un court article sur une fonctionnalité à venir en C++20 : les initializers dans les range-based for, proposés avec la P0614 | “Range Based for statements with initializers”.
Herb Sutter rapporte dans son blog que la P0614 a été acceptée pour le C++20. Cette proposition vise à ajouter une expression d’initialisation optionnelle aux range-based for, ce qui est dans la continuité de C++17 qui l’avait déjà ajoutée pour les if et les switch (ce dont j’ai déjà pu parler dans un précédent article).
La proposition donne l’exemple incorrect suivant :
1 2 3 4 5 6 7 8 9 10 11 12 13 |
class T { std::vector<int> data_; public: std::vector<int>& items() { return data_; } // ... }; T foo(); // retourne une prvalue for(auto& c : foo().items()) { // Invalide ! // ... } |
Le code précédent est en effet équivalent à celui-ci en C++17 :
1 2 3 4 5 6 7 8 9 |
{ auto && __range = foo().items(); // (1) problème auto __begin = std::begin(__range); auto __end = std::end(__range); for ( ; __begin != __end; ++__begin) { c = *__begin; // ... } } |
foo() retourne un T temporaire, de fait, la référence retournée par T::items() n’est plus valide après l’instruction (1), on a ici un comportement indéterminé. Il faut alors passer par une variable intermédiaire :
1 2 3 4 |
auto f = foo(); for(auto& c : f.items()) { // ... } |
Et on retombe ici dans le même problème de portée qui avait motivé les initializers pour if et switch : la portée n’est plus liée à la boucle. On se voit donc obligé de contenir la déclaration dans un bloc artificiel de manière à limiter la portée de la variable :
1 2 3 4 5 6 |
{ auto f = foo(); for(auto& c : f.items()) { // ... } } |
C++20 propose donc d’ajouter une séquence d’initialisation optionnelle :
1 2 3 |
for(auto f = foo(); auto& c : f.items()) { // ... } |
La portée de la variable intermédiaire est ici limitée à celle de la boucle comme on le souhaitait. Cela nous permet d’ailleurs d’être conforme à cette recommandation des CppCoreGuidelines.
Comme le précise le standard, l’expression d’initialisation n’est pas dans la même région déclarative que celle de l’expression de range. Ainsi, de la même manière que auto x = foo(); for (auto x : x) était valide, for(auto x = foo(); auto x = x) l’est également, le x de l’initialisation étant masqué à l’intérieur de la boucle.
A la fin de la proposition, l’auteur Thomas Köppe indique deux idées d’ajouts futurs : l’expression d’incrémentation for(init; auto x : c; inc) et les initializers pour le do while.
Voilà qui clôt donc cet article sur le C++20, merci de votre lecture et à bientôt !
Références et ressources
- [EN] open-std.org | P0614r0 “Range-base for statements with initializers”
- [EN] cppreference.com | range-based for loop
- [EN] Herb Sutter’s Blog | Trip report : Fall ISO C++ standards metting (Albuquerque)
- [EN] CppCoreGuidelines | Declare names in for statement initializers and condition to limit scope
You must log in to post a comment.