web-dev-qa-db-ger.com

Was ist der Zweck von std :: launder?

P0137 führt die Funktionsvorlage std::launder Ein und nimmt in den Abschnitten über Gewerkschaften, Lebensdauer und Zeiger viele, viele Änderungen an der Norm vor.

Was ist das Problem, das dieses Papier löst? Was sind die Änderungen an der Sprache, die ich beachten muss? Und was machen wir launder?

211
Barry

std::launder heißt treffend, aber nur, wenn Sie wissen, wofür es ist. Es führt eine Speicherwäsche durch .

Betrachten Sie das Beispiel im Papier:

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

U u = {{ 1 }};

Diese Anweisung führt eine Gesamtinitialisierung durch und initialisiert das erste Mitglied von U mit {1}.

Da n eine const -Variable ist, kann der Compiler davon ausgehen, dass u.x.n soll immer 1 sein.

Was passiert also, wenn wir dies tun:

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

Da 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 legaler Code. Das neue Objekt hat das n -Element 2.

Also sag mir ... was wird u.x.n Rückkehr?

Die offensichtliche Antwort ist 2. Aber das ist falsch, weil der Compiler annehmen darf, dass es sich um eine echte const -Variable handelt (nicht nur um eine const&, aber eine Objektvariable , die als const) deklariert wurde, wird sich niemals ändern . Aber wir haben es nur 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 möglich ist. Und ein const Mitglied zu haben, ist einer der disqualifizierenden Faktoren.

Also ... wie können wir über u.x.n richtig?

Wir müssen unser Gedächtnis waschen:

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

Geldwäsche wird eingesetzt, um zu verhindern, dass Personen nachvollziehen können, woher Sie Ihr Geld haben. Speicherwäsche wird verwendet, um zu verhindern, dass der Compiler nachverfolgt, woher Sie Ihr Objekt bezogen haben, und um Optimierungen zu vermeiden, die möglicherweise nicht mehr zutreffen.

Ein weiterer disqualifizierender Faktor ist das Ändern des Objekttyps. 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 teilt uns mit, dass Sie, wenn Sie ein neues Objekt in den Speicher des alten aufnehmen, nicht über Zeiger auf das alte auf das neue Objekt zugreifen können. Mit launder können wir das umgehen.

218
Nicol Bolas