Ist dieser Code unabhängig von der Kopierqualität gut definiert?

8

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 ?

ist

Ich habe versucht, mit gcc und clang mit -O0 und -O3 zu kompilieren, und die Ergebnisse sehen immer noch wie beabsichtigt aus.

    
Ruslan 23.10.2015, 14:48
quelle

5 Antworten

3

Das ist ein wohlgeformter Code, Optimierungen können wohlgeformten Code nicht brechen, da dies gegen die Als-ob-Regel . Was uns das sagt:

  

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]

    
Shafik Yaghmour 23.10.2015, 15:00
quelle
4

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.

    
SergeyA 23.10.2015 14:50
quelle
3

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!

    
gsamaras 23.10.2015 14:52
quelle
2

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.

    
Christian Hackl 23.10.2015 14:58
quelle
1

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.)

    
Yakk 23.10.2015 19:20
quelle