URL: https://linuxfr.org/news/cpp17-fixe-l-ordre-d-evaluation-des-expressions Title: C++17 fixe l’ordre d’évaluation des expressions Authors: Adrien Jeser Oliver, Davy Defaud, Benoît Sibaud et palm123 Date: 2016年11月25日T16:57:06+01:00 License: CC By-SA Tags: cpp17, c++ et c++17 Score: 56 Le C++ est un langage bien présent et depuis longtemps dans les logiciels libres (environnements de bureau, outils bureautiques, navigateurs Web...). L’an 2017 approche à grands pas avec la promesse d’un tout nouveau **C++17**. Pour finir l’année, voici le [calendrier de l’Avent][avent] du C++ avec des dépêches pédagogiques sur ce qui nous attend en 2017. Après [deux][1] [dépêches][2] de mise‐en‐bouche, nous entrons enfin dans le vif du sujet avec deux [spécifications techniques][TS] concernant l’*ordre d’évaluation des expressions*. Allez, c’est parti !   **ᕕ(ᐛ)ᕗ** [avent]: https://fr.wikipedia.org/wiki/Calendrier_de_l'Avent [1]: https://linuxfr.org/news/les-coulisses-du-standard-cpp [2]: https://linuxfr.org/news/c-17-genese-d-une-version-mineure [TS]: https://linuxfr.org/news/les-coulisses-du-standard-cpp#technical-specification-ts [![Bjarne propose de changer le C++ pour corriger son livre qu'il tient dans ses mains](http://cpp-frug.github.io/materials/images/cpp17-bjarne-ordre-evaluation.png)](https://github.com/cpp-frug/materials/blob/gh-pages/images/README.md#un-bogue-dans-le-livre-de-bjarne) ---- [Dépêche préliminaire dans « Les coulisses du standard C++ » (CC BY-SA 4.0)](https://linuxfr.org/news/les-coulisses-du-standard-cpp) [Dépêche de mise en bouche racontant la genèse du C++17 (CC BY-SA 4.0)](https://linuxfr.org/news/c-17-genese-d-une-version-mineure) [Contenu markdown de cette dépêche sur le dépôt Git du C++FRUG](https://github.com/cpp-frug/materials/blob/gh-pages/news/2016-12-01_Cpp17-ordre-evaluation.md) [Mars 2016 : Réunion pour dresser un premier contour du C++17 (Herb Sutter)](https://isocpp.org/blog/2016/03/trip-report-jax-sutter) [Mars 2016 : Détail de cette même réunion du comité de normalisation du C++ (Botondballo)](https://botondballo.wordpress.com/2016/03/21/trip-report-c-standards-meeting-in-jacksonville-february-2016/) [Juin 2016 : Réunion pour finaliser le périmètre C++17 (Herb Sutter)](https://herbsutter.com/2016/06/30/trip-report-summer-iso-c-standards-meeting-oulu/) [Liste concise des nouveautés C++17 sur StackOverflow (Yakk, CC BY-SA 3.0)](http://stackoverflow.com/a/38060437/938111) [Liste partielle des nouveautés C++17 sur MeetingC++ (Jens Weller)](https://meetingcpp.com/index.php/br/items/final-features-of-c17.html) [Article Wikipédia C++17 (CC BY-SA 3.0)](https://en.wikipedia.org/wiki/C%2B%2B17) [Principaux apports des C++11/14/17 (Anthony Calandra, MIT)](https://github.com/AnthonyCalandra/modern-cpp-features/blob/master/README.md) ---- Série de dépêches C++ ===================== Cette dépêche fait partie de toute une série disponible également sur [le dépôt Git][dG] du [*Groupe des Utilisateurs C++ Francophone*][CppFRUG]. Alors que cet article restera figé sur le site *LinuxFr.org*, il continuera d’évoluer sur le dépôt Git. Merci de nous aider [à maintenir ce document à jour][md] avec vos questions/suggestions/corrections. L’idée est de partager ce contenu libre et de créer/enrichir des articles Wikipédia quand la licence [sera CC BY-SA 4.0](https://meta.wikimedia.org/wiki/Terms_of_use/Creative_Commons_4.0/fr).   [dG]: https://github.com/cpp-frug/materials [CppFRUG]: http://linuxfr.org/news/douzieme-rencontre-parisienne-c-mercredi-27-avril-2016#historique-des-rencontres-c-francophones [md]: https://github.com/cpp-frug/materials/blob/gh-pages/news/README.md#pour-contribuer Publication | Dépêche ------------------|------------------------------------------------- [20 août 2016][L1]|[Les coulisses du standard C++][G1] [2 oct. 2016][L2]|[Genèse du C++17][G2] 1er déc. 2016 |[C++17 fixe l’ordre d’évaluation des expressions][G3] à venir... |... d’autres dépêches ... [en 2017][LE] |[Faut‐il continuer à apprendre le C++ ?][GE] Initialement, nous allions publier [une grosse dépêche super trop longue](https://github.com/cpp-frug/materials/blob/gh-pages/news/2016_n3_Cpp17_Nouveautes-du-langage.md). Puis, fin novembre, nous prenions conscience que les lecteurs apprécieraient plutôt une petite dépêche par jour, d’où l’idée de faire le calendrier de l’Avent du C++.   **(ღˇ◡ˇ)~♥** [L1]: https://linuxfr.org/news/les-coulisses-du-standard-cpp [L2]: https://linuxfr.org/news/cpp17-ameliore-les-lambdas-et-attribut-s [LE]: https://linuxfr.org/news/faut-il-continuer-a-apprendre-le-cpp [G1]: https://github.com/cpp-frug/materials/blob/gh-pages/news/2016_n1_Coulisses-du-standard.md [G2]: https://github.com/cpp-frug/materials/blob/gh-pages/news/2016_n2_Cpp17_Genese-d-une-version-mineure.md [G3]: https://github.com/cpp-frug/materials/blob/gh-pages/news/2016-12-01_Cpp17-ordre-evaluation.md [GE]: https://github.com/cpp-frug/materials/blob/gh-pages/news/2017_n14_Faut-il-apprendre-le-Cpp.md Spécifications Techniques ========================= Deux [*TS*](https://linuxfr.org/news/les-coulisses-du-standard-cpp#technical-specification-ts) ont été amendés par le comité de normalisation du C++ afin de fixer l’ordre d’évaluation des expressions : * [**P0145**](https://wg21.link/p0145) définit les changements nécessaires au standard C++ ; * [**P0400**](https://wg21.link/p0400) reformule une phrase de cette précédente *TS* P0145. Anecdote ======== Le livre mythique [*The C++ Programming Language*][CppPL] de l’inventeur du C++, [Bjarne Stroustrup][BS] contient une erreur subtile à la [page 1046][p1046] du paragraphe **36.3.6 _STL‐like Operations_** (quatrième édition publiée en 2013). Sauras‐tu la retrouver ? Voici l’extrait en question : [CppPL]: https://fr.wikipedia.org/wiki/The_C%2B%2B_Programming_Language [BS]: https://fr.wikipedia.org/wiki/Bjarne_Stroustrup [p1046]: http://www.ebooksbucket.com/the-c-programming-language-4th-edition-b198>The `replace()` replaces one substring with another and adjusts the `string`’s size accordingly. For example:>>```cpp>void f()>{> string s = "but I have heard it works even if you don't believe in it";> s.replace(0,4,""); // erase initial "but "> s.replace(s.find("even"),4,"only");> s.replace(s.find(" don't"),6,""); // erase by replacing with ""> assert(s=="I have heard it works only if you believe in it");>}>```>> A `replace()` returns a reference to the object for which it was called. This can be used for chaining operations:>>```cpp>void f2()>{> string s = "but I have heard it works even if you don't believe in it";> s.replace(0,4,"").replace(s.find("even"),4,"only").replace(s.find(" don't"),6,"");> assert(s=="I have heard it works only if you believe in it");>}>``` Pas trouvé ? Pas d’inquiétude, aucun humain n’avait trouvé cette erreur. Bien après la publication de ce livre, cette erreur a été trouvée, non pas par une nouvelle forme d’[intelligence artificielle][IA], mais juste par un [outil d’analyse statique de code source][as] au nez et à la barbe des pointures C++ aguerries. [IA]: https://fr.wikipedia.org/wiki/Intelligence_artificielle [as]: https://fr.wikipedia.org/wiki/Analyse_statique_de_programmes Explications ============ Pour des questions de performance, le standard C++ (avant C++17) indique que c’est le compilateur qui optimise l’ordre d’évaluation du chaînage et des paramètres de fonction. Le standard utilise le terme *unsequenced* (séquencement non défini). Le C et le C++ partagent ensemble cette règle. Donc, l’expression `replace(find()).replace(find())` dans la fonction `f2()` peut être évaluée dans des ordres différents. En théorie, la variable `s` pourrait donc contenir différents résultats. Et c’est aussi le cas en pratique : Compilateur | Résultat contenu par la variable `s` -------------------------|------------------------------------------ [GCC][g] et
[MSVC][v]| `I have heard it works evenonlyyou donieve in it` [LLVM/Clang][c] | `I have heard it works only if you believe in it` [g]: http://coliru.stacked-crooked.com/a/a6035cb6e64f038f [c]: http://coliru.stacked-crooked.com/a/84408d788238bacd [v]: http://rextester.com/VKFPX23982a Détails ======= Ci‐dessous, la première ligne déclare et initialise un objet `std::string`. La seconde ligne cherche et remplace plusieurs caractères de cette `std::string` en utilisant le chaînage des fonctions [`replace`](http://en.cppreference.com/w/cpp/string/basic_string/replace) : ```cpp std::string s = "but I have heard it works even if you don’t believe in it"; s.replace(0,4,"").replace(s.find("even"),4,"only").replace(s.find(" don’t"),6,""); ``` Intuitivement, on s’attendrait à évaluer les arguments des fonctions comme `find("even")` juste avant d’appeler `replace(resultat,4,"only")`. Mais ce n’est pas le cas avant C++17, ces arguments peuvent être évalués dans différents ordres. Plus de détails sont donnés par [Shafik Yaghmour](http://stackoverflow.com/a/27158813/938111) (en anglais). Le tableau ci‐dessous présente sur chacune des sept lignes, un ordre d’appel possible selon les standards C++ (avant C++17) et C (en supposant que ce soit une `struct string` avec des pointeurs de fonction) : 1er appel | 2e appel | 3e appel | 4e appel | 5e appel --------------|-------------|-------------|-------------|------------- `find(" don’t")`|`find("even")`|`replace(0,4,"")`|`replace(f,4,"only")`|`replace(f,6,"")` `find("even")`|`find(" don’t")`|`replace(0,4,"")`|`replace(f,4,"only")`|`replace(f,6,"")` `find(" don’t")`|`replace(0,4,"")`|`find("even")`|`replace(f,4,"only")`|`replace(f,6,"")` `find("even")`|`replace(0,4,"")`|`find(" don’t")`|`replace(f,4,"only")`|`replace(f,6,"")` `replace(0,4,"")`|`find(" don’t")`|`find("even")`|`replace(f,4,"only")`|`replace(f,6,"")` `replace(0,4,"")`|`find("even")`|`find(" don’t")`|`replace(f,4,"only")`|`replace(f,6,"")` `replace(0,4,"")`|`find("even")`|`replace(f,4,"only")`|`find(" do’'t")`|`replace(f,6,"")` C++17 n’autorise qu’une seule possibilité, la dernière du tableau, et correspond à celle de la fonction `f()` du livre : ```cpp s.replace(0, 4, ""); s.replace(s.find("even"), 4, "only"); s.replace(s.find(" don't"), 6, ""); ``` Autres exemples =============== Par exemple, dans l’expression `f().g(h())` la fonction `f()` peut être appelée avant ou après `h()`. Le standard C++ fait la différence entre *unspecified* (non spécifié) et *unsequenced* (non séquencé). Ce comportement est bien spécifié, donc jusqu’à C++14, c’est *unsequenced*. À partir de C++17, c’est `f()` avant `h()` (*sequenced before*). ```cpp // Avant C++17, f() peut être appelée avant ou après h() f().g( h() ); // C++17 est plus intuitif : f() est toujours appelée avant h() ``` C’est aussi le cas de l’expression `std::cout << f() << g() << h();` dont les trois fonctions peuvent être appelées dans n’importe quel ordre : ```cpp // Avant C++17, le compilateur décide l’ordre d’évaluation de f(), g() et h() std::cout << f() << g() << h() << std::endl; // C++17 fixe l’ordre intuitif : d’abord f(), puis g() et enfin h() ``` Encore d’autres exemples que le C++ partage avec le C : ```cpp std::map m; m[0] = m.size(); std::cout << m[0]; // Affiche 0 ou 1 ? // Clang : 0 // GCC : 1 // MSVC++ : 0 ``` ```cpp int i = 0; std::cout << i << ' ' << i++; // Affiche 0 0 ou 1 0 ? // Clang : 0 0 // GCC : 1 0 // MSVC++ : 1 0 ``` ```cpp int i = 0; i = i++ + 1; // unsequenced std::cout << i; // Quelle valeur ? // Clang : 1 mais avertit : multiple unsequenced modifications to 'i' // GCC-6.2 : 1 mais avertit : operation on 'i' may be undefined ``` Ci‐dessus, pour toutes les versions du C++ et du C, l’opération est *unsequenced* et non pas *undefined*, comme GCC-6.2 le laisse supposer : ```cpp int i = 0; i = ++i, i++, i++, ++i, ++i, ++i, i++; std::cout << i; // Quelle valeur ? // Piège, toujours 7 car c'est "sequenced before" // GCC-6.2 avertit : operation on 'i' may be undefined ``` Notons que GCC-6.2 suppose encore que l’opération est *undefined*, alors que dans ce dernier cas l’opération est *sequenced before*, quelle que soit la version du C++ (et même du C). Conséquence =========== Donc, de nombreux codes source sont potentiellement truffés de ces pièges, ce qui est également le cas quand `std::future` est utilisé. Tout le monde se fait avoir, débutants comme experts. Et le comité de normalisation du C++ a donc amendé sans trop discuter ce *TS*, afin de fixer l’ordre d’évaluation dans certains cas. Et c’est justement cet exemple du livre [*The C++ Programming Language*](https://fr.wikipedia.org/wiki/The_C%2B%2B_Programming_Language) qui illustre le paragraphe **5.2.2 _Function call_** (page 107) du [standard C++ (brouillon de juillet 2016)](http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2016/n4606.pdf). Nouvelle règle ============== L’évaluation est : * de la gauche vers la droite pour les expressions suffixées. Ceci inclut les appels de fonction et la section des membres ; * l’affectation de la droite vers la gauche (`a = b = c = d`) ; * les opérateurs de décalage (*shift operators*) de la gauche vers la droite. En revanche, lorsqu’une surcharge d’opérateur est invoquée, la priorité arithmétique est utilisée. Peut‐être que le code généré sera moins performant, et que les standards C et C++ divergent un peu plus, mais, au moins, le langage C++ devient un peu plus intuitif. ** ̄\\_(ツ)_/ ̄** [L1]: https://linuxfr.org/news/les-coulisses-du-standard-cpp [L2]: https://linuxfr.org/news/cpp17-ameliore-les-lambdas-et-attribut-s [LE]: https://linuxfr.org/news/faut-il-continuer-a-apprendre-le-cpp [G1]: https://github.com/cpp-frug/materials/blob/gh-pages/news/2016_n1_Coulisses-du-standard.md [G2]: https://github.com/cpp-frug/materials/blob/gh-pages/news/2016_n2_Cpp17_Genese-d-une-version-mineure.md [G3]: https://github.com/cpp-frug/materials/blob/gh-pages/news/2016-12-01_Cpp17-ordre-evaluation.md [GE]: https://github.com/cpp-frug/materials/blob/gh-pages/news/2017_n14_Faut-il-apprendre-le-Cpp.md Appel à participation ===================== La précédente dépêche a reçu [227 commentaires](https://linuxfr.org/news/c-17-genese-d-une-version-mineure#droit-dauteur-licences-remerciements), soit un volume dix fois supérieur à la dépêche elle‐même. Tous ces commentaires cachent tout de même quelques joyeux [trolls](https://fr.wikipedia.org/wiki/Troll_%28Internet%29) velus ! Quand on pense à toute cette énergie dépensée et toutes ces heures consacrées à rédiger ces 227 commentaires ! Avec le recul, nous aurions pu concentrer tout cet investissement dans une dépêche collaborative du style « _Aujourd’hui, est‐il pertinent de choisir le C++ pour une nouvelle application ?_ ». [![Panneau « Please Do Not Feed the Trolls »](https://upload.wikimedia.org/wikipedia/commons/1/19/Trolls.jpg)](https://commons.wikimedia.org/wiki/File:Trolls.jpg) | [![Panneau Troll barré](https://upload.wikimedia.org/wikipedia/commons/e/ea/DoNotFeedTroll.svg)](https://commons.wikimedia.org/wiki/File:DoNotFeedTroll.svg) ----|---- Ne pas nourrir les trolls|Ne pas nourrir les trolls Mais il n’est jamais trop tard ! Aussi nous proposons vous de rédiger la dépêche « [_Faut‐il continuer à apprendre le C++ ?_](https://linuxfr.org/news/faut-il-continuer-a-apprendre-le-c) » Les nombreux commentaires de la dépêche précédente méritent d’y être copiés. Malheureusement, ceux‐ci sont rarement sous licence compatible CC BY-SA 4.0. Ceci est donc un appel à tous leurs auteurs pour les copier dans cette dépêche afin de la nourrir. Ainsi, nous pourrons les structurer et proposer des réponses concises, claires et utiles à tous. Merci et à vos claviers !   Réutilisation ============= Le texte de cette dépêche est protégé par ~~le [droit d’auteur](https://fr.wikipedia.org/wiki/Droit_d%27auteur#Droit_d.E2.80.99auteur_traditionnel_vs._licences_de_libre_diffusion)~~ la [gauche d’auteur](https://fr.wikipedia.org/wiki/Gauche_d'auteur) et réutilisable sous licence [CC BY-SA 4.0](https://creativecommons.org/licenses/by-sa/4.0/deed.fr). Les images utilisées sont aussi sous licence libre (cliquer sur l’image pour plus de détails). Donc, n’hésitez pas à réutiliser ce contenu libre pour créer, par exemple, des supports de formation, des présentations (_Meetups_), des publications sur d’autres blogs, des articles pour des magazines, et aussi un article C++17 sur Wikipédia dès que [Wikipédia passera de la licence CC-BY-SA 3.0 à la CC-BY-SA 4.0](https://meta.wikimedia.org/wiki/Terms_of_use/Creative_Commons_4.0) (le contenu de cette dépêche utilise la version la CC-BY-SA 4.0). Les auteurs =========== Par respect de la licence, merci de [créditer](https://fr.wiktionary.org/wiki/cr%C3%A9diter#Verbe) les auteurs : * les principaux auteurs sont [Adrien Jeser](https://linuxfr.org/users/jeser) et [Oliver H.](https://linuxfr.org/users/oliver_h)’; * les nombreux autres contributeurs ayant contribué sur l’ancêtre de cette dépêche ou sur le [dépôt Git](https://github.com/cpp-frug/materials) sont : [_eggman_](https://linuxfr.org/users/eggman), [Yves Bourguignon](https://linuxfr.org/users/biomin), [_Storm_](https://linuxfr.org/users/storm--2), [_gorbal_](https://linuxfr.org/users/gorbal), [_palm123_](https://linuxfr.org/users/palm123), [_khivapia_](https://linuxfr.org/users/khivapia), [_BAud_](https://linuxfr.org/users/baud), [_Segfault_](https://linuxfr.org/users/elly), [Benoît Sibaud](https://linuxfr.org/users/oumph), [Lucas](https://linuxfr.org/users/george), [_cracky_](https://linuxfr.org/users/cracky), [Martin Peres](https://linuxfr.org/users/mupuf), [_RyDroid_](https://linuxfr.org/users/rydroid), [_olibre_](https://github.com/olibre) et [_Guss_](https://github.com/GuillaumeDua). Continuer à améliorer ce document ================================= Malgré tout le soin apporté, il reste certainement des oublis, des ambiguïtés, des fôtes... Bien que cette dépêche restera figée sur le site *LinuxFr.org*, il est possible de continuer à l’enrichir sur le [dépôt Git](https://github.com/cpp-frug/materials/blob/gh-pages/news/2016_n3_Cpp17_Nouveautes-du-langage.md) du [**Groupe des utilisateurs C++ francophone**](http://linuxfr.org/news/douzieme-rencontre-parisienne-c-mercredi-27-avril-2016#historique-des-rencontres-c-francophones) (C++FRUG). C’est donc sur ce dépôt que se trouvent les versions les plus à jour.   **(ღ ̆⌣ ̆ღ)** La suite ======== Nous venons de découvrir un changement important au niveau du langage. La dépêche suivante nous dévoilera une autre nouveauté du C++17. Chère lectrice, cher lecteur _LinuxFr.org_. Tu souhaites apporter ta pierre à cet édifice ? Rejoins‐nous dans l’[espace de rédaction collaborative sur _LinuxFr.org_](https://linuxfr.org/redaction) (un [compte](https://linuxfr.org/compte/inscription) est nécessaire pour y accéder). À suivre...

AltStyle によって変換されたページ (->オリジナル) /