2016-06-26 34 views
15

http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2015/p0135r0.htmlWird 'Garantiertes Kopieren' (P0135, C++ 1z) möglicherweise ABI-Bruch erfordern?

Der obige Vorschlag für ‚Guaranteed Copy Elision‘ wurde in die C++ gewählt Papier in der Sitzung Juni 2016 arbeiten in Oulu, Finnland, die für die Veröffentlichung wurde dann als Komitee-Entwurf gestimmt. Hoffentlich führt das im nächsten Jahr zur Veröffentlichung als C++ 17 Standard.

Der Vorschlag verdeutlicht verschiedene Wertkategorien für temporäre Objekte, um in bestimmten Anwendungsfällen das Fehlen von Kopierkonstruktoraufrufen zu erzwingen.

Meine Frage ist „könnte diese neue Anforderung brechen könnte ABI-Kompatibilität für Compiler, die möglicherweise vorher nicht Kopie elision unter diesen Umständen getan haben, oder haben sie in einer Art und Weise umgesetzt, die nicht mit den neuen Anforderungen kompatibel sein wird?“

Ich denke an Dinge wie Initialisierungen, die Elide kopiert, wenn die Erstellung eines Objekts inline ausgeführt werden kann, aber nicht beim Überqueren der Grenzen der Kompilierungseinheit.

+4

Ich kann mir eine ABI vorstellen (wenn eine lächerliche), die durch diese Änderung gebrochen wäre. Wäre das ausreichend, oder suchen Sie nach tatsächlichen Instanzen eines ABI, die tatsächlich von einem tatsächlichen Nicht-Spielzeug-Compiler verwendet werden, der bricht? – Yakk

+4

Wie können Sie etwas brechen, das nicht existiert? – user3104201

+0

"* Ich denke an Dinge wie Initialisierungen, die Elide kopiert, wenn die Erstellung eines Objekts inline ausgeführt werden kann, aber nicht beim Überqueren von Compilierungseinheitsgrenzen. *" Ich kenne keine solchen Umstände, wo das möglich wäre. –

Antwort

11

Wenn eine Funktion aufgerufen wird, muss die Funktion einen Wert zurückgeben. Dieser Wert benötigt Speicher zum Leben, aber der Rückgabewert muss die Funktion selbst übertreffen. Der ABI definiert, wie das alles funktioniert. Im Allgemeinen geschieht dies, indem der Aufrufer der Funktion ein Stück Speicher der Größe/Ausrichtung für den Rückgabewert bereitstellt.

Wenn also eine Funktion einen Wert berechnet und zurückgibt, muss er (theoretisch) diesen berechneten Wert in den Rückgabewertspeicher kopieren. Und wenn der Aufrufer es abruft, muss es (theoretisch) diesen Rückgabewertspeicher in ein anderes Stapelobjekt zur späteren Verwendung kopieren.

Nicht garantierte Kopie elision sagt, dass keine dieser Kopien notwendig ist. Auf der Seite der Rückgabefunktion darf der Compiler den Rückgabewertspeicher intern verwenden, wenn er diesen Wert generiert, sodass die Rückgabeanweisung nichts kopieren muss. Und auf der Empfangsseite, wenn der Speicher verwendet wird, um ein Stapelobjekt zu initialisieren, muss es nicht in diesen Speicher kopiert werden.

Garantierte Kopie elision sagt, dass, wenn die empfangende Seite ein Objekt desselben Typs initialisiert, der Empfänger nicht berücksichtigt, ob das Objekt einen Kopier/Verschiebe-Konstruktor hat. Daher wird der Code, der eine Funktion wie auto t = Func(); aufruft, ihn nicht als eine mögliche Kopieroperation in t behandeln. Der Compiler, der diesen Code verarbeitet, ruft Func mit dem Rückgabewertspeicher auf, der sich in dem Stapelspeicher für t befindet.

Und auf der Angerufenen Seite, wenn Sie einen prvalue direkt zurückgeben, dann ist es nicht notwendig, dass ein Copy/Move-Konstruktor existiert. Der Angerufene erstellt den Pr-Wert direkt im Rückgabewert-Speicher.

Hier ist die Sache: ABIs kümmern sich nicht darum. Alles, was ABI interessiert, ist Low-Level-Speicher. Das heißt, solange der Aufrufer Rückgabewertspeicher der geeigneten Größe und Ausrichtung weitergibt und der Aufgerufene diesen Speicher mit einem Objekt des geeigneten Typs initialisiert ... ist der ABI egal.

Wenn der Aufrufer diesen Rückgabewertspeicher für spätere Operationen verwenden möchte, ist das für den ABI in Ordnung. Wenn der Angerufene Daten direkt in den Rückgabewertspeicher initialisieren möchte, anstatt sie zu kopieren, wird der ABI dies nicht bemerken.

Der ABI definiert die Schnittstelle; Was Sie mit dieser Schnittstelle tun, bleibt Ihnen überlassen.

Als Beispiel betrachten Sie die Itanium ABI on return values. Es erlaubt Klassentypen, in Registern gespeichert zu werden, aber nur, wenn sie triviale Kopie-/Bewegungskonstruktoren haben. Andernfalls müssen sie unabhängig von ihrem Inhalt in dem Speicher aufgebaut werden, der von der aufrufenden Funktion bereitgestellt wird. Wenn die Klasse trivial kopierbar ist, können Sie den Unterschied zwischen Elision und Nicht-Elision nicht unterscheiden.

Die einzige Möglichkeit für ein ABI könnte ein Problem für diese Funktion sein, wenn die ABI willkürlich entschieden, wo der Rückgabewert (und vermutlich die Parameter) relativ zueinander gespeichert werden. Das heißt, der ABI zwingt den Aufrufer, das Objekt an einer bestimmten Position relativ zu den Parametern auf dem Stapel zu platzieren.

Könnte solch ein ABI existieren? Ich habe keine besonderen Kenntnisse zu sagen, dass es nicht möglich ist. Macht es? Ich bezweifle es eher, da solch ein ABI im Allgemeinen die Elision ziemlich schwierig machen würde.

+0

Die Frage läuft dann darauf hinaus, ob es irgendwelche vorhandenen Compiler gibt, die den Rückgabewertspeicher auf irgendeine dumme Weise implementieren, die entweder mit dem Aufgerufenen verschmelzen würde, der das Ergebnis direkt schreibt, und oder der Aufrufer das Ergebnis direkt daraus verwendet. – Novelocrat

+0

@Novelocrat: Der ABI definiert, wie Werte über Funktionsaufrufe hinweg mechanisch übergeben werden. Die einzige Möglichkeit, die es vermasselt, ist, wenn ein Compiler nicht dem ABI folgt. Das ist ein viel größeres Problem, als die garantierte Elision nicht korrekt umzusetzen. –

+3

@ Nicol: Was ist, wenn der Aufrufer nicht einen beliebigen Ort angeben darf, an dem der Rückgabewert platziert wird, sondern der ABI ihn relativ zu etwas anderem (Parameterpositionen, Absenderadresse usw.) angibt? Dann kann der Anrufer möglicherweise eine nachfolgende Übertragung von dem ABI-definierten Ort (der durch zusätzliche Funktionsaufrufe überschrieben werden kann, bevor die Lebensdauer des Objekts endet) nicht zu einem längerlebigen Ort vermeiden. –