Betrachten Sie diesen Code:
%Vor%Man würde eine Ausgabe wie folgt erwarten:
%Vor% und das ist wirklich was ich bekomme. Aber aufgrund von copy elision könnte sich out
an derselben Stelle im Speicher befinden wie in
und dazu führen, dass die letzte Ausgabezeile x: 2, y: 2
?
Ich habe versucht, mit gcc und clang mit -O0
und -O3
zu kompilieren, und die Ergebnisse sehen immer noch wie beabsichtigt aus.
Das ist ein wohlgeformter Code, Optimierungen können wohlgeformten Code nicht brechen, da dies gegen die Als-ob-Regel
Insbesondere müssen sie die Struktur der abstrakten Maschine nicht kopieren oder emulieren. Eher konform Implementierungen sind erforderlich, um (nur) das beobachtbare Verhalten der abstrakten Maschine wie erläutert zu emulieren unter
mit Ausnahme für Kopierfehler:
[...] Eine Implementierung darf die Konstruktion einer Klasse kopieren / verschieben weglassen Objekt, selbst wenn der Konstruktor für die Kopier- / Verschiebeoperation und / oder den Destruktor für das Objekt ausgewählt wurde Nebenwirkungen haben. [...]
Aber die Regeln für die Reihenfolge müssen noch eingehalten werden. Wenn wir zum Standardentwurf gehen, sehen wir, dass die Zuweisung nach der Auswertung der linken und rechten Operanden aus Abschnitt 5.17 folgt:
In allen Fällen wird die Zuordnung nach dem Wert sequenziert Berechnung des rechten und linken Operanden und vor der Wertberechnung des Zuweisungsausdrucks
und wir wissen, dass der Rumpf der Funktion in Bezug auf andere Bewertungen, die nicht spezifisch mit dem Funktionsaufruf aus Abschnitt 1.9 sequenziert sind, unbestimmt sequenziert ist:
Jede Auswertung in der aufrufenden Funktion (einschließlich anderer Funktionsaufrufe), die nicht speziell anders ist sequenziert vor oder nach der Ausführung des Körpers der aufgerufenen Funktion ist unbestimmt sequenziert mit bezüglich der Ausführung der aufgerufenen Funktion 9
und unbestimmt sequenziert bedeutet:
Die Auswertungen A und B sind unbestimmt sequenziert, wenn entweder A sequenziert wird, bevor B oder B vor A sequenziert werden, aber es ist nicht spezifiziert, welches. [Hinweis: Indeterminierte sequenzielle Auswertungen können nicht überlappen, aber beide könnten zuerst ausgeführt werden. -Hinweis]
Nein, kann es nicht. Optimierungen können wohlgeformten Code nicht brechen, und dieser Code ist wohlgeformt.
BEARBEITEN: Kleines Update. Natürlich geht meine Antwort davon aus, dass der Compiler selbst fehlerfrei ist, worüber man natürlich nur beten kann:)
EDIT2: Einige Leute sprechen über Nebenwirkungen in Kopierern und dass sie schlecht sind. Natürlich sind sie nicht schlecht. So wie ich es sehe, ist es in C ++ nicht garantiert, dass eine bekannte Anzahl von temporären Objekten erstellt wird. Sie werden garantiert, dass jedes temporäre Objekt zerstört wird. Während Optimierungen die Anzahl der temporären Objekte durch Kopieren reduzieren können, dürfen sie diese auch erhöhen! :) Solange Ihre Nebenwirkungen mit dieser Tatsache im Hinterkopf stehen, sind Sie gut.
Nein, konnte es nicht!
Optimierung bedeutet nicht, dass Sie undefiniertes Verhalten in einem gut geschriebenen (nicht schlecht konditionierten) Code erhalten.
Überprüfen Sie diese Referenz:
konforme Implementierungen sind erforderlich, um (nur) das beobachtbare Verhalten der abstrakten Maschine zu emulieren, wie unten erläutert. ...
Eine konforme Implementierung, die ein wohlgeformtes Programm ausführt, muss das gleiche beobachtbare Verhalten wie eine der möglichen Ausführungssequenzen der entsprechenden Instanz der abstrakten Maschine mit dem gleichen Programm und der gleichen Eingabe erzeugen. ...
Das beobachtbare Verhalten der abstrakten Maschine ist ihre Folge von Lese- und Schreibvorgängen in flüchtigen Daten und Aufrufen von Bibliotheks-E / A-Funktionen. ...
aus dieser Antwort .
In dieser Antwort sehen Sie einen Fall, in dem copy-elision möglicherweise eine andere Ausgabe erzeugt!
Das Kopieren darf nur dann aufgehoben werden, wenn Sie Nebeneffekte in Ihrem Kopierkonstruktor haben. Dies ist kein Problem, da Kopierkonstruktoren immer frei von Nebenwirkungen sein sollten.
Nur zur Veranschaulichung, hier ist ein Kopierkonstruktor mit Nebenwirkungen. Das Verhalten dieses Programms hängt in der Tat von Compiler-Optimierungen ab, d. H. Ob eine Kopie tatsächlich erstellt wurde oder nicht:
%Vor%Der Code, den Sie gezeigt haben, ist frei von solchen Nebenwirkungen und das Verhalten Ihres Programms ist klar definiert.
Elision ist die Verschmelzung von Objektlebensdauern und Identitäten.
Elision kann zwischen einem temporären (anonymen Objekt) und dem benannten Objekt, das (direkt) zum Konstruieren verwendet wird, und zwischen einer funktionslokalen Variablen, die kein Funktionsargument ist, und dem Rückgabewert einer Funktion auftreten.
Elision pendelt tatsächlich. (Wenn Objekt A und B zusammen elided sind, und B und C zusammen elided sind, dann werden A und C zusammen elided).
Um den Rückgabewert einer Funktion mit einer Variablen außerhalb der Funktion anzugeben, müssen Sie diesen Rückgabewert direkt aus dem Rückgabewert konstruieren. Während die konstruierte Variable in einigen Kontexten benannt sein kann, bevor sie konstruiert wird, ist deren Verwendung (ähnlich wie im obigen Code) undefiniertes Verhalten vor dem Auftreten des Konstruktors.
Dieser Konstruktor der externen Variable wird nach dem Body von func
und somit nach dem Aufruf von func
sequenziert. Es konnte also nicht vorkommen, dass func
aufgerufen wird.
Hier ist ein Beispiel für einen Fall, in dem wir eine Variable vor ihrer Erstellung benannt und übergeben haben zu func
, dann initialisiere die Variable mit dem Rückgabewert von func
. Es sieht so aus, als ob der Compiler in diesem Fall nicht elide ist, aber wie unten in den Kommentaren bemerkt, tat die ID tatsächlich: Mein Aufruf von UB verbarg die Elision. (Die Faltung war ein Versuch, den Compiler daran zu hindern, die Werte von test.x und test.y vorzurechnen.)
Tags und Links c++ undefined-behavior copy-elision