web-dev-qa-db-fra.com

Quel est le but de std :: launder?

P0137 introduit le modèle de fonction std::launder et apporte de nombreuses modifications à la norme dans les sections concernant les unions, la durée de vie et les pointeurs.

Quel est le problème résolu par ce document? Quels sont les changements de langue que je dois connaître? Et que sommes-nous laundering?

211
Barry

std::launder porte bien son nom, mais seulement si vous savez à quoi il sert. Il effectue un nettoyage de la mémoire .

Prenons l'exemple de l'article:

struct X { const int n; };
union U { X x; float f; };
...

U u = {{ 1 }};

Cette instruction effectue l'initialisation de l'agrégat en initialisant le premier membre de U avec {1}.

Puisque n est une variable const, le compilateur est libre de supposer que u.x.n doit toujours être 1.

Alors qu'est-ce qui se passe si nous faisons cela:

X *p = new (&u.x) X {2};

Comme X est trivial, il n'est pas nécessaire de détruire l'ancien objet avant d'en créer un autre à la place. Il s'agit donc d'un code parfaitement légal. Le nouvel objet aura son membre n soit 2.

Alors, dis-moi ... que reviendra u.x.n?

La réponse évidente sera 2. Mais c'est faux, car le compilateur est autorisé à supposer qu'une variable vraiment const (pas simplement un const&, mais une variable d'objet déclarée const) ne changera jamais . Mais nous venons de le changer.

[basic.life]/8 définit les circonstances dans lesquelles il est correct d'accéder à l'objet nouvellement créé par le biais de variables/pointeurs/références à l'ancien. Et avoir un membre const est l’un des facteurs disqualifiants.

Alors ... comment parler de u.x.n correctement?

Nous devons blanchir notre mémoire:

assert(*std::launder(&u.x.n) == 2); //Will be true.

Le blanchiment d’argent est utilisé pour empêcher les gens de rechercher l’origine de votre argent. Le nettoyage de la mémoire est utilisé pour empêcher le compilateur de tracer d'où vous avez récupéré votre objet, le forçant ainsi à éviter toute optimisation qui pourrait ne plus être appliquée.

Si vous modifiez le type de l'objet, vous pouvez également vous exclure. std::launder peut aider ici aussi:

aligned_storage<sizeof(int), alignof(int)>::type data;
new(&data) int;
int *p = std::launder(reinterpret_cast<int*>(&data));

[basic.life]/8 nous dit que, si vous allouez un nouvel objet dans le stockage de l'ancien, vous ne pouvez pas accéder au nouvel objet par le biais de pointeurs sur l'ancien. launder nous permet de contourner cela.

218
Nicol Bolas