
Barry
P0137 führt die Funktionsvorlage ein std::launder
und nimmt viele, viele Änderungen am Standard in den Abschnitten über Gewerkschaften, Lebensdauer und Zeiger vor.
Welches Problem löst dieses Papier? Welche Sprachänderungen muss ich beachten? Und was sind wir launder
ing?

Nicol Bola
std::launder
ist treffend benannt, allerdings nur, wenn Sie wissen, wofür es ist. Es führt Gedächtniswäsche.
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 Aggregatinitialisierung durch und initialisiert das erste Mitglied von U
mit {1}
.
Weil n
ist ein const
Variable, kann der Compiler davon ausgehen u.x.n
soll immer 1 sein.
Was passiert also, wenn wir dies tun:
X *p = new (&u.x) X {2};
Weil X
trivial ist, müssen wir das alte Objekt nicht zerstören, bevor wir an seiner Stelle ein neues erstellen, also ist dies ein vollkommen legaler Code. Das neue Objekt wird seine haben n
Mitglied sein 2.
Also sag mir… was wird u.x.n
Rückkehr?
Die offensichtliche Antwort wird 2 sein. Aber das ist falsch, weil der Compiler davon ausgehen darf, dass a true ist const
Variable (nicht nur eine const&
sondern eine Objektvariable erklärt const
) wird sich nie ändern. Aber wir haben es einfach geändert.
[basic.life]/8 buchstabiert die Umstände, wann es in Ordnung ist, auf das neu erstellte Objekt über Variablen/Zeiger/Referenzen auf das alte zuzugreifen. Und mit einem const
Mitglied ist einer der disqualifizierenden Faktoren.
Also… wie können wir darüber reden u.x.n
richtig?
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 nachvollziehen können, woher Sie Ihr Geld haben. Speicherwäsche wird verwendet, um dies zu verhindern Compiler davon abzuhalten, zu verfolgen, woher Sie Ihr Objekt haben, und es so zu zwingen, alle Optimierungen zu vermeiden, die möglicherweise nicht mehr gelten.
Ein weiterer disqualifizierender Faktor ist, wenn Sie die Art des Objekts ändern. std::launder
kann hier auch 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 im Speicher des alten zuweisen, nicht auf das neue Objekt durch Zeiger auf das alte zugreifen können. launder
ermöglicht es uns, dem auszuweichen.

einpoklum
std::launder
ist eine Fehlbezeichnung. Diese Funktion führt die Gegenteil der Wäsche: Es Böden den Speicher, auf den gezeigt wird, um jegliche Erwartung des Compilers bezüglich des Werts, auf den gezeigt wird, zu beseitigen. Es schließt jegliche Compiler-Optimierungen basierend auf solchen Erwartungen aus.
Daher könnte der Compiler in der Antwort von @NicolBolas davon ausgehen, dass ein Teil des Speichers einen konstanten Wert enthält. oder ist nicht initialisiert. Sie sagen dem Compiler: “Dieser Ort ist (jetzt) verschmutzt, gehen Sie nicht davon aus.”
Wenn Sie sich fragen, warum der Compiler von vornherein immer an seinen naiven Erwartungen festhält und Sie dafür auffällig Dinge beschmutzen müssten, sollten Sie diese Diskussion lesen:
Warum `std::launder` einführen, anstatt den Compiler sich darum kümmern zu lassen?
… was mich zu dieser Ansicht von was führt std::launder
bedeutet.
Ich denke, es gibt zwei Zwecke std::launder
.
- Eine Barriere für
konstante Faltung/Fortpflanzung, einschließlich Devirtualisierung.
- Eine Barriere für eine feinkörnige objektstrukturbasierte Aliasanalyse.
Barriere für überaggressive konstante Faltung / Ausbreitung (aufgegeben)
In der Vergangenheit erlaubte der C++-Standard Compilern anzunehmen, dass der Wert eines const-qualifizierten oder referenzierten nicht statischen Datenmembers, der auf gewisse Weise erhalten wurde, unveränderlich ist, selbst wenn sein enthaltendes Objekt nicht konstant ist und durch Platzierung neu wiederverwendet werden kann.
In C++17/P0137R1, std::launder
wird als eine Funktionalität eingeführt, die die zuvor erwähnte (Fehl-)Optimierung (CWG 1776), was benötigt wird std::optional
. Und wie besprochen in P0532R0portable Implementierungen von std::vector
und std::deque
kann auch brauchen std::launder
auch wenn es sich um C++98-Komponenten handelt.
Glücklicherweise ist eine solche (Fehl-)Optimierung verboten RU007 (enthalten in P1971R0 und C++20). AFAIK gibt es keinen Compiler, der diese (Fehl-) Optimierung durchführt.
Barriere für Devirtualisierung
Ein virtueller Tabellenzeiger (vptr) kann während der Lebensdauer seines enthaltenden polymorphen Objekts, das für die Devirtualisierung benötigt wird, als konstant betrachtet werden. Da vptr kein nichtstatisches Datenelement ist, dürfen Compiler weiterhin eine Devirtualisierung auf der Grundlage der Annahme durchführen, dass vptr nicht geändert wird (d. h. entweder das Objekt noch in seiner Lebensdauer ist oder von einem neuen Objekt der wiederverwendet wird gleichen dynamischen Typs) in einigen Fällen.
Für einige ungewöhnliche Verwendungen, die ein polymorphes Objekt durch ein neues Objekt eines anderen dynamischen Typs ersetzen (gezeigt Hier), std::launder
wird als Barriere für die Devirtualisierung benötigt.
IIUC Clang implementiert std::launder
(__builtin_launder
) mit dieser Semantik (LLVM-D40218).
Barriere für objektstrukturbasierte Aliasanalyse
P0137R1 ändert auch das C++-Objektmodell, indem es Zeiger-Interkonvertibilität einführt. Eine solche IIUC-Änderung ermöglicht eine „objektstrukturbasierte Aliasanalyse“, die in vorgeschlagen wird N4303.
Infolgedessen macht P0137R1 die direkte Verwendung der Dereferenzierung a reinterpret_cast
‘d Zeiger von einem unsigned char [N]
Array undefiniert, selbst wenn das Array Speicher für ein anderes Objekt des richtigen Typs bereitstellt. Und dann std::launder
wird für den Zugriff auf das verschachtelte Objekt benötigt.
Diese Art der Alias-Analyse erscheint überaggressiv und kann viele nützliche Codebasen zerstören. AFAIK wird es derzeit von keinem Compiler implementiert.
Zusammenhang mit typbasierter Aliasanalyse/striktem Aliasing
IIUC std::launder
und typbasierte Aliasanalyse/striktes Aliasing stehen in keinem Zusammenhang. std::launder
erfordert, dass sich ein lebendes Objekt des richtigen Typs an der angegebenen Adresse befindet.
Es scheint jedoch, dass sie versehentlich in Clang (LLVM-D47607).
9630500cookie-checkWas ist der Zweck von std::launder?yes
Fragen Sie nach dem Papier selbst oder nach
std::launder
?std::launder
wird verwendet, um “einen Zeiger auf ein Objekt zu erhalten, das im Speicher erstellt wurde und von einem vorhandenen Objekt desselben Typs belegt ist, selbst wenn es const- oder Referenzelemente hat.”– txtechhelp
8. September 2016 um 4:25 Uhr
sinnvoll Verknüpfung zum Thema. Auch diese Frage stackoverflow.com/questions/27003727/…
– Paul Rooney
8. September 2016 um 4:30 Uhr
Diese wurde nun in VC2017 in Version 15.7.0 veröffentlicht
– Damian
8. Mai 2018 um 0:47 Uhr
Laut std sind Zeiger triviale Typen, sodass Launder nichts bewirkt. 😉
– Neugieriger
1. Dezember 2018 um 10:26 Uhr