std::launder
wird treffend benannt, allerdings nur, wenn Sie wissen, wofür es ist. Es führt Speicherwäsche durch .
Betrachten Sie das Beispiel in der Arbeit:
struct X { const int n; };
union U { X x; float f; };
...
U u = {{ 1 }};
Diese Anweisung führt eine aggregierte Initialisierung durch und initialisiert das erste Mitglied von U
with {1}
.
Da n
eine ist const
variabel, ist der Compiler frei davon ausgehen , dass u.x.n
muss immer 1 sein.
Was passiert also, wenn wir dies tun:
X *p = new (&u.x) X {2};
Da dies X
trivial ist, müssen wir das alte Objekt nicht zerstören, bevor wir an seiner Stelle ein neues erstellen. Dies ist also ein vollkommen gesetzlicher Code. Das n
Mitglied des neuen Objekts ist 2.
Also sag mir ... was wird u.x.n
zurückkehren?
Die offensichtliche Antwort lautet 2. Aber das ist falsch, da der Compiler davon ausgehen darf, dass sich eine echte const
Variable (nicht nur eine const&
, sondern eine deklarierte Objektvariable const
) niemals ändern wird . Aber wir haben es einfach geändert.
[basic.life] / 8 beschreibt die Umstände, unter denen der Zugriff auf das neu erstellte Objekt über Variablen / Zeiger / Verweise auf das alte Objekt in Ordnung ist. Und ein const
Mitglied zu haben, ist einer der disqualifizierenden Faktoren.
Also ... wie können wir u.x.n
richtig darüber reden ?
Wir müssen unser Gedächtnis waschen:
assert(*std::launder(&u.x.n) == 2); //Will be true.
Geldwäsche wird verwendet, um zu verhindern, dass Personen nachverfolgen, woher Sie Ihr Geld haben. Die Speicherwäsche wird verwendet, um zu verhindern, dass der Compiler nachverfolgt, woher Sie Ihr Objekt haben, und um Optimierungen zu vermeiden, die möglicherweise nicht mehr zutreffen.
Ein weiterer disqualifizierender Faktor ist, wenn Sie den Typ des Objekts ändern. std::launder
kann auch hier helfen:
aligned_storage<sizeof(int), alignof(int)>::type data;
new(&data) int;
int *p = std::launder(reinterpret_cast<int*>(&data));
[basic.life] / 8 sagt uns, dass Sie, wenn Sie ein neues Objekt im Speicher des alten Objekts zuweisen, nicht über Zeiger auf das alte auf das neue Objekt zugreifen können. launder
erlaubt uns, das zu umgehen.
std::launder
?std::launder
wird verwendet, um "einen Zeiger auf ein Objekt zu erhalten, das im Speicher erstellt wurde, der von einem vorhandenen Objekt desselben Typs belegt wird, selbst wenn es Konstanten- oder Referenzelemente enthält."