Hello.
Désolé pour ma relecture très tardive. D’autant, que je pose des questions pas si triviales. ^^'
Je passe en coup de vent car je me posais une question non triviale relativement à des aspects de progression pédagogique. Est-ce que l’on peut présenter la sémantique de déplacement toute seule, ou est-ce un sujet advanced pas pour débutants?
On a deux grandes applications:
- l’optim qui cherche à réduire les co^uts (désolé, je ne n’ai toujours pas trouvé comment configurer correctement les touches mortes de mon clavier) quand on ne peut pas profiter d’une élision de copie
- les transferts de responsabilités sur les handles, à commencer par
unique_ptr
. Indirectement, est-ce qu’il ne faudrait pas présenter comment unique_ptr
s’utilise avant de parler de la sémantique de déplacement?
A propos de ce chapitre, une nouvelle relecture.
un simple tableau d’entier
"entier" devrait prendre la marque du pluriel.
Matrice(Matrice && matrice) noexcept = default;
Hum… Est-ce qu’il ne faudrait pas rappeler que si ce code ici ne plante pas c’est parce que tous les attributs de la matrice sont déplaçables sans fragiliser d’invariants du genre: "un seul responsable des libérations". Et là, un joli piège: il y un invariant dans la matrice: buffer.size() == nbligs * nbcols… Donc, il faut bien définir les 2 opérations de déplacement.
Les rvalues, acronymes de right-handed values, désignent […] mais aussi les objets qui arrivent en fin de vie, comme le temporaire m1 + m2 qu’on a vu en exemple dans la section précédente.
J’aime bien résumer ces temporaires en "plus généralement: des expressions". Ca distingue assez bien de "variable" je trouve. D’ailleurs à la lecture, étrange. "expression" est le terme que je réserve aux rvalues pour employer "variable (ou expression [OK, expression n’est pas si faux] qui s’apparente à une variable)" pour les lvalues.
pile & tas: La portion qu’utilise le programme est découpée en deux grandes zones.
En vrai, il y a le static storage aussi. Je dirais plut^ot: "intéressons-nous à deux des grandes zones de la mémoire vive qu’un programme est amené à manipuler", par exemple.
Ainsi, quand on créé un tableau redimensionnable avec std::vector, ou qu’on manipule un fichier avec std::fstream, les données sont stockées dans le tas 2
Euh… pour les flux, ce n’est pas une propriété première. Au mieux, c’est un détail d’implémentation relativement aux mises en cache avant flush.
la libération doit aussi être explicite.
Quand on décide d’allouer à la main, hein?!
Si l’attribut est stocké sur le tas
Je n’aime pas ce raccourci. L’attribut est toujours dans l’espace de son englobant. Maintenant certains attributs sont en fait des identifiants (handles, adresses mémoire) qui font référence à des zones ailleurs (tas, sockets, fichiers). Un peu comme une fiche de livre dans une bibliothèque: déplacer la fiche ne déplace pas le livre, le numéro du livre se retrouve sur la fiche qui est rangée ailleurs (dans le casier des emprunts p.ex.).
Peut-^etre faudrait-il introduire les termes des notions de copie superficielle (shallow) et de copie profonde (deep)? Et donc la copie des numériques (vrais nombres, ou handles/adresses) est toujours superficielle, tout comme leur déplacement qui est en fait une copie en C++ : pour des raisons de performance, il n’y a pas de remise à zéro.
C’est à nous de coller une sémantique de déplacement sur nos handles en les enrichissant.
Ca mériterait un dessin dès maintenant.
La sémantique de déplacement ne garantie donc pas l’absence de copie. Si l’on n’a que des attributs qui utilisent la pile (comme Fraction), on ne gagne rien par rapport à la copie. Par contre, dès lors qu’il y a des attributs qui utilisent le tas (comme std::vector ou std::string), on peut gagner en performance.
Ce n’est pas tant une question de pile et de tas, mais de ce que représente l’attribut. Si on met de c^oté les attributs à qui nous avons collé une sémantique de déplacement ou qui en obtiennent une de leurs attributs & parents (NB: cela ne règle pas le cas de l’invariant multi-attributs des matrices, des tableaux & chaines…), la question devient plut^ot est-ce que l’attribut ne serait pas un machin (genre un entier, un ISBN…) qui sert à identifier un truc qui vit sa vie ailleurs (RAM/fichier/socket, livre…).
Alors dans ce cas, il peut y avoir gain à déplacer. Autrement, toute la charge utile de l’objet englobant se trouve ^etre uniquement dans cet englobant.
Dites-moi si je ne suis pas clair.
PS:
struct S {
std::array<int, 42> t;
};
auto s = new S;
s->t est bien dans le tas et pourtant new S(std::move(*s))
reviendra à une copie.