std::launder
porte bien son nom, mais seulement si vous savez à quoi il sert. Il effectue le blanchiment de la mémoire .
Prenons l'exemple du document:
struct X { const int n; };
union U { X x; float f; };
...
U u = {{ 1 }};
Cette instruction effectue une initialisation agrégée, initialisant le premier membre de U
with {1}
.
Parce qu'il n
s'agit d'une const
variable, le compilateur est libre de supposer qu'il u.x.n
doit toujours être 1.
Alors, que se passe-t-il si nous faisons cela:
X *p = new (&u.x) X {2};
Parce que X
c'est trivial, nous n'avons pas besoin de détruire l'ancien objet avant d'en créer un nouveau à sa place, c'est donc un code parfaitement légal. Le nouvel objet aura son n
membre 2.
Alors dis-moi ... qu'est-ce qui u.x.n
reviendra?
La réponse évidente sera 2. Mais c'est faux, car le compilateur est autorisé à supposer qu'une const
variable vraiment (pas simplement une const&
, mais une variable d'objet déclarée const
) ne changera jamais . Mais nous venons de le changer.
[basic.life] / 8 décrit les circonstances dans lesquelles il est OK d'accéder à l'objet nouvellement créé via des variables / pointeurs / références à l'ancien. Et avoir un const
membre est l'un des facteurs de disqualification.
Alors ... comment parler 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 retrouver d'où vous avez tiré votre argent. Le blanchiment de la mémoire est utilisé pour empêcher le compilateur de retrouver d'où vous venez votre objet, le forçant ainsi à éviter toute optimisation qui pourrait ne plus s'appliquer.
Un autre des facteurs disqualifiants est si vous changez le type de l'objet. std::launder
peut aussi aider ici:
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 via des pointeurs vers l'ancien. launder
nous permet de contourner cela.
std::launder
?std::launder
est utilisé pour "obtenir un pointeur vers un objet créé dans le stockage occupé par un objet existant du même type, même s'il a des membres const ou reference".