URL: https://linuxfr.org/news/cpp17-garantit-le-court-circuit-de-copie-suite-de-la-precedente-depeche Title: C++17 garantit le court-circuit de copie (suite de la précédente dépêche) Authors: Adrien Jeser Oliver, Davy Defaud, David Marec, Snark, Benoît Sibaud et lmg HS Date: 2016年12月08日T13:56:57+01:00 License: CC By-SA Tags: c++17 et c++ Score: 28 Le calendrier de l’Avent du C++ continue son bonhome de chemin. Chaque jour, ou presque, une nouvelle surprise est offerte aux lecteurs de *LinuxFr.org*. La dépêche sur l’[élision de la copie](http://linuxfr.org/news/cpp-se-court-circuite-le-constructeur-de-copie) nous a mis l’eau à la bouche : > ***Comment résoudre le dilemme entre cette optimisation et le fait de ne pas pouvoir en bénéficier dans un code portable ?*** Alors, entrons dans les entrailles de la spécification technique [P0135](http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2016/p0135r1.html) guidés par cette dépêche pédagogique, et découvrons comment le C++ s’améliore de version en version. [![Une fille nerd s’électrocute en touchant la nouvelle tour C++17 de sa voisine avec garantie de court-circuit de copie](https://cpp-frug.github.io/materials/images/nerdettes_cpp17-courtcircuite-constructeur-copie_copyright-oliverh-cc-by-sa-3.png)](https://github.com/cpp-frug/materials/blob/gh-pages/images/nerdettes_cpp17-courtcircuite-constructeur-copie_copyright-oliverh-cc-by-sa-3.png) ---- [1er décembre « C++17 fixe l’ordre d’évaluation des expressions » ](http://linuxfr.org/news/c-17-fixe-l-ordre-d-evaluation-des-expressions) [2 décembre « C++17 indique la disponibilité des entêtes »](http://linuxfr.org/news/c-17-indique-la-disponibilite-des-en-tetes-header) [5 décembre « C++17 branche à la compilation »](http://linuxfr.org/news/c-17-branche-a-la-compilation-if-constexpr) [7 décembre « C++17 exprime la virgule flottante en hexadécimal »](http://linuxfr.org/news/c-17-exprime-la-virgule-flottante-en-hexadecimal-et-offre-des-cadeaux-aux-lecteurs-de-linuxfr-org) [11 décembre « C++ se court-circuite le constructeur de copie »](http://linuxfr.org/news/cpp-se-court-circuite-le-constructeur-de-copie) [Spécification Technique P0135 sur la garantie de court-circuit du constructeur de copie](http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2016/p0135r1.html) ---- La problématique ================ Nous avons vu dans la dépêche [_C++ se court‐circuite le constructeur de copie_](https://linuxfr.org/news/cpp-se-court-circuite-le-constructeur-de-copie), les problèmes de l’élision du constructeur de copie. En effet, cette optimisation est optionnelle : selon le compilateur, sa version, ses options de compilation, ou encore selon le code source, l’élision sera appliquée ou pas. Donc, dans un code portable, de façon générale, nous ne pouvons pas nous passer des constructeurs de copie et de déplacement, car l’élision pourrait ne pas avoir lieu. Cela interdit aux types _indéplaçables_ d’avoir des fonctions qui retournent par valeur, telles que les fabriques. ```cpp int n; // compte le nombre d’appels du constructeur de copie struct A { A(int) {} A(const A&) { ++n; } }; int main() { A a = A( A( A( A(42) ) ) ); return n; // valeur non déterminée avant C++17 } // toujours 0 avec C++17 ``` L’astuce de la TS P0135 ======================= La spécification technique [P0135](http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2016/p0135r1.html) ne rend pas obligatoire l’élision. Pour la garantir, cette TS redéfinit la taxonomie des expressions dans le but d’éviter toute création inutile d’objet temporaire. Le cas des _Named Return Value Optimizations_ n’est pas concerné par cette spécification technique, faute de pouvoir apporter une garantie simple. Une taxonomie des expressions ? ============================= La taxonomie est la science du classement, pour identifier et décrire le vivant. Un taxon est un groupe d’organismes classés ensemble parce qu’ils ont des caractéristiques communes. Comme des poupées russes, un taxon peut contenir un sous‐ensemble plus petit de taxons. Voir aussi le documentaire [_Espèces d’espèces_](http://videotheque.cnrs.fr/doc=2007). Le C++ reprend ce principe pour classer ses expressions. La classification historique (C++98) définit deux taxons : ***lvalue*** et ***rvalue***. À l’origine, ces termes ont été choisis car, dans les cas simples, le ***lvalue*** se trouve à gauche (_**l**eft **value**_) et le ***rvalue*** (_**r**ight **value**_) se trouve à droite. Mais cette disposition n’est pas toujours vraie. left_value = right_value; \ \ `--se trouve `--se trouve à gauche à droite Chacun des taxons a des propriétés spécifiques. Par exemple, seul **lvalue** peut être la cible d’une affectation. ![Taxonomie C++98](https://cpp-frug.github.io/materials/schemas/taxnomie-des-expressions/cpp98.png) **Note :** La norme définit un objet comme une zone mémoire pouvant contenir des données. Une taxonomie contre‐intuitive ============================== La taxonomie du C++11 a inscrit la nouvelle catégorie ***xvalue***, nécessaire pour la [sémantique de déplacement](https://fr.wikipedia.org/wiki/C%2B%2B11#S.C3.A9mantique_des_RValues_Reference.2FMove) (*move semantics*). Ce qui a rendu l’évaluation des expressions C++ plus confuse et difficile à s’approprier : Taxon | Caractères discriminants -----------|-------------------------------------- ***lvalue*** | Désigne une fonction ou un objet ***xvalue*** | Un objet proche de la fin de vie et certains types d’expressions impliquant des références ***rvalue*** ***rvalue*** | Une ***xvalue***, un objet temporaire, un sous‐objet ou une valeur pas associé avec objet. ***prvalue***| Une ***rvalue*** qui n’est pas une ***xvalue*** ***glvalue***| Taxon supérieur d’une ***lvalue*** et ***xvalue*** Ainsi, il y a des contradictions dans de nombreux cas. Par exemple, une expression qui crée un objet temporaire est une ***rvalue***. Alors que selon les critères discriminant, elle aurait pu tout aussi bien être une ***lvalue***. Autre paradoxe, une expression qui construit un objet puis accède à un membre, telle que `A().a` est une ***xvalue***. En effet, l’objet construit `A()` expire à la fin de l’évaluation de l’expression. Or, dans une situation où il est indéplaçable, c’est contradictoire avec une ***xvalue***, ce qui sous‐entend être déplaçable. Donc, elle devrait être une ***prvalue***. On y perd son latin ! Ci‐dessous, un schéma très simplifié, pour mieux comprendre ce classement : ![Taxonomie C++11](https://cpp-frug.github.io/materials/schemas/taxnomie-des-expressions/cpp11.png) Redéfinir pour garantir l’élision de copie ========================================== La spécification technique propose de faire la distinction entre les expressions ***prvalue*** et ***glvalue***. Désormais, une ***glvalue*** définit la localisation d’un objet, une ***prvalue*** son initialisation : Taxon | Caractères discriminants -----------|-------------------------------------- ***glvalue*** | Identifie une fonction, un objet, un champ de bits ***prvalue*** | Initialise un objet, un champ de bits ***xvalue*** | une **glvalue** dont la ressource peut être réutilisée ***lvalue*** | Une **glvalue** qui n’est pas une ***xvalue*** ***rvalue*** | Une **prvalue** qui n’est pas une ***xvalue*** ![Taxonomie C++17](https://cpp-frug.github.io/materials/schemas/taxnomie-des-expressions/cpp17.png) ```cpp struct X { int n; }; extern X x; X{4}; // prvalue car initialise un objet temporaire X x.n; // glvalue car localise l'attribut n de x X{4}.n; // glvalue car localise l'attribut n de X{4} using T = X[2]; T{{5}, {6}}; // prvalue car initialise un tableau de X ``` Si une ***prvalue*** est utilisée pour initialiser un objet de même type, il le sera directement. Par conséquent, l’initialisation de la valeur de retour d’une fonction avec un temporaire entraîne l’initialisation directe de la valeur, sans copie, ni déplacement. Cela signifie que le constructeur de copie ou de déplacement de l’objet n’a plus besoin d’être défini. ```cpp struct NonDeplacable { NonDeplacable(int); NonDeplacable(NonDeplacable &) = delete; NonDeplacable(NonDeplacable &&) = delete; std::array arr; }; NonDeplacable make() { return NonDeplacable(42); // Construit directement l'objet renvoyé } auto nm = make(); // Construit directement l'objet renvoyé dans 'nm' NonDeplacable x = {5}; // Ok avant C++17 NonDeplacable x = 5; // Équivaut à NonDeplacable x = NonDeplacable(5) // Erreur avant C++17 parce qu'il crée un objet non déplaçable // Maintenant, plus d'erreur ``` Ce changement est sans conséquence pour l’optimisation [_Named Return Value Optimization_](https://en.wikipedia.org/wiki/NRVO) (NRVO). Comme mentionné précédemment, la modification implique seulement les ***prvalues***. Avec la NRVO, la valeur retournée est une ***glvalue***. ```cpp A f() { A a; return a; // Retourne une glvalue } // Donc sans garantie d'élision int main() { A a = f(); } ``` Et les xvalues ? ================ La définition d’une ***xvalue*** gagne en clarté. Désormais, elle désigne une expression dont la ressource (les données) peut être réutilisée. Ce qui correspond bien à un objet déplaçable, dont ses données peuvent être réutilisées dans un autre objet. Dernier changement, le compilateur est autorisé à convertir l’initialisation d’un objet temporaire (***prvalue***) vers une ***xvalue***. ```cpp struct X { int n; } int k = X().n; // X() est une prvalue convertie en xvalue. ``` Conclusion ========== Nous venons d’aborder un sujet complexe du C++. Nous pourrions trouver le C++ beaucoup trop compliqué. Mais cette dépêche a montré que le standard se simplifie. Un standard C++ plus compréhensif, de meilleures optimisations et un code plus intuitif. Que demander de plus ? 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.   **(ღ ̆⌣ ̆ღ)** 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 et corrections. L’idée est de partager ce contenu libre et de créer et 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).   **٩(•‿•)۶** Appel à contribution ==================== Nous nous efforçons de vous fournir une dépêche de qualité chaque jour ouvré. Et, en plus, avec une illustration sympathique. Mais cela demande beaucoup de travail, et tenir les délais n’est pas toujours simple. Merci de nous donner un coup de main, que ce soit sur la correction orthographique, le dessin, la lecture des spécifications techniques, la rédaction d’une nouvelle dépêche à intégrer au calendrier de l’Avent du C++. Tu peux aussi apporter ta contribution à la dépêche [_Faut‐il continuer à apprendre le C++ ?_](https://linuxfr.org/news/faut-il-continuer-a-apprendre-le-c) Rejoins‐nous sur la [page du groupe de travail C++](https://linuxfr.org/redaction/news/groupe-de-travail-depeches-c-17) sur _LinuxFr.org_ (un [compte](https://linuxfr.org/compte/inscription) est nécessaire pour y accéder). À suivre...

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