Wer kopiert den Rückgabewert einer Funktion?

8

Ist es der Anrufer oder der Angerufene, der den Rückgabewert einer Funktion kopiert oder verschiebt? Zum Beispiel, wenn ich die pop () - Funktion einer Warteschlange implementieren möchte, wie dies

%Vor%

ist der Destruktor meines Zielfernrohrwächters, der nach dem Kopieren des Frontelements aufgerufen wird?

BEARBEITEN: Folgefrage: Wäre die Zeile

%Vor%

sei jetzt stark ausnahmesicher?

    
Ralph Tandetzky 02.07.2013, 08:53
quelle

2 Antworten

9

Der Rückgabewert wird kopiert, bevor die lokalen Variablen den Gültigkeitsbereich verlassen. Das Kopieren / Verschieben kann an einen temporären Speicherort (Stapel oder Register) oder direkt an den Puffer oder die bevorzugten Register des Aufrufers erfolgen - das ist ein Optimierungs- / Inlining-Problem.

Wo ein temporärer Ort beteiligt ist, muss der Compiler eine Arbeitsteilung zwischen dem Aufrufer und dem Aufgerufenen arrangieren, und es gibt eine Reihe von OS- und Binärobjekt / ausführbare Format-spezifischen Konventionen für Rückgabewerte (und natürlich Funktionsparameter). , so dass Bibliotheken / Objekte, die mit einem Compiler kompiliert wurden, typischerweise immer noch mit einem anderen verwendet werden können.

  

Würde die Zeile ...

%Vor%
  

... streng sicher sein?

Wenn pop_front() nicht throw angenommen werden kann, ist der interessante Fall, dass ein temporärer Speicherort zurückgegeben wird, von dem der Wert nach dem Zurückgeben der Funktion erneut in den aufrufenden Puffer kopiert wird. Es scheint mir, dass Sie dagegen nicht ausreichend geschützt haben. Elision (der Angerufene, der den Rückgabewert direkt im Ergebnispuffer / Register (n) des Aufrufers konstruiert) ist erlaubt, aber nicht erforderlich.

Um dies zu erkunden, habe ich den folgenden Code geschrieben:

%Vor%

Kompilieren mit g++ -fno-elide-constructors , meine Ausgabe (mit zusätzlichen Kommentaren) war:

%Vor%

Es ist klar, dass die Zuweisung nach f() left scope erfolgte: Eine Ausnahme davon wäre, nachdem Ihr scope guard (hier durch Y dargestellt) zerstört wurde.

Dasselbe passiert, wenn main X x = f(); oder X x(f()); enthält, außer dass es der Kopierkonstruktor ist, der nach der Zerstörung der Variablen f() -local aufgerufen wird.

(Ich schätze, dass das Verhalten eines Compilers manchmal eine schlechte Grundlage dafür ist, darüber nachzudenken, ob etwas vom Standard benötigt wird, aber umgekehrt ist es wesentlich zuverlässiger: Wenn es nicht funktioniert, ist auch der Compiler kaputt - was ist relativ selten - oder der Standard erfordert es nicht.Hier, das Compiler-Verhalten wird nur verwendet, um anekdotische Gewicht zu meinem Eindruck der Anforderungen des Standards hinzuzufügen.

Fummelige Details für Neugierige: Nicht, dass es normalerweise nützlich wäre, Code zu haben, der nur auf eine Art aufgerufen werden kann, aber etwas, das vielleicht ist, ist const X& x = f(); , als const -Referenz verlängert die Lebensdauer des Temporären, aber ich kann mich selbst nicht davon überzeugen, dass der Standard erfordert, dass das temporäre, dessen Lebensdauer verlängert wird, das temporäre ist, in das die Funktion ohne zusätzliche Kopie kopiert wird; für das, was wenig wert ist - es "arbeitete" in meinem Programm und interessanterweise belegt das temporäre die gleiche Stapelposition, die verwendet wird, wenn ein Rückgabewert ausgegeben wird, was darauf hindeutet, dass f() code effektiv mit einer Fähigkeit zum Ellen und der Option -f-no-elide-constructors kompiliert ist nicht so sehr, eine Optimierung zu deaktivieren, als eine Pessimierung hinzuzufügen: zusätzlichen Stapelraum für ein temporäres zu lassen, bevor die Funktion aufgerufen wird, dann den zusätzlichen Code hinzufügt, um davon zu kopieren und das temporäre zu zerstören, dann den Stapelzeiger neu einstellen ....

    
Tony Delroy 02.07.2013, 09:00
quelle
6

Die Kopie des Rückgabewerts wird vom Aufrufer ausgeführt und muss vor dem Aufruf von Destruktoren erstellt werden, da sonst der Wert / Inhalt einer lokal erstellten Variablen nicht zurückgegeben werden kann.

Hier ist der relevante Abschnitt in der Norm: Abschnitt 12.4, Punkt 11 (Destruktoren)

  

Destruktoren werden implizit aufgerufen

     
  • für konstruierte Objekte mit automatischer Speicherdauer (3.7.3), wenn der Block, in dem ein Objekt erstellt wird, endet (6.7)
  •   

Ich habe versucht, einen Ort zu finden, an dem es heißt, dass die "Rückkehr vor der Zerstörung" stattfindet, aber es gibt das nicht so klar wieder, wie ich es möchte [es sei denn, ich vermisse etwas].

    
Mats Petersson 02.07.2013 09:00
quelle

Tags und Links