Wird der Destruktor eines lokalen Objekts in einer Schleife garantiert vor der nächsten Iteration aufgerufen?

11

Wenn ich eine Schleife habe und innerhalb dieser Schleife eine neue Stapelvariable erstelle (ohne sie auf dem Heap zuzuweisen und die Variable, die sie im Schleifenkörper deklariert hält), wird der Destruktor dieses Objekts garantiert aufgerufen, bevor die nächste Iteration beginnt oder möglicherweise Schleife Abwickeln durch den Compiler etwas daran ändern?

user1282931
quelle
1
Das Abrollen der Schleife an sich ändert nichts an der Ausführungsreihenfolge. Allerdings Schleife Parallelisierung könnte dies tun.
Adrian Mole

Antworten:

8

Von n4800:

§6.3.3 Block - Scope :

Ein in einem Block (8.3) deklarierter Name ist lokal für diesen Block. es hat Blockumfang. Sein potenzieller Geltungsbereich beginnt am Deklarationspunkt (6.3.2) und endet am Ende seines Blocks. Eine im Blockbereich deklarierte Variable ist eine lokale Variable.

§10.3.6 Zerstörer :

Ein Destruktor wird implizit aufgerufen, [...] wenn der Block, in dem ein Objekt erstellt wird, beendet wird (8.7).

§4.1.1 Abstrakte Maschine :

Diese Bestimmung wird manchmal als "Als-ob" -Regel bezeichnet, da es einer Implementierung freigestellt ist, eine Anforderung dieses Dokuments zu ignorieren, solange das Ergebnis so ist, als ob die Anforderung eingehalten worden wäre, soweit dies aus dem beobachtbaren Verhalten von bestimmt werden kann das Programm .

[Hervorhebung von mir]

Also ja. Ihre Variable verlässt am Ende der Schleife (die ein Block ist) den Gültigkeitsbereich und daher wird ihr Destruktor aufgerufen , soweit jeder, der das Verhalten des Programms beobachtet, dies beurteilen kann .

Paul Evans
quelle
1
Nichts drin darüber, WANN der Destruktor aufgerufen wird.
stark
2
@stark Was ihnen dies ermöglicht, ist die Als-ob-Regel. Der Standard legt nur das Verhalten der abstrakten Maschine fest. Ich bin mir nicht sicher, ob es notwendig ist, hier in den Antworten auf all diese Details einzugehen.
Max Langhof
2
@stark Dies ist, IMO, für die Frage irrelevant. Sie können auch sagen, dass Destruktoren inline und daher überhaupt nicht bearbeitet werden können call. Oder wenn sie effektiv (als ob-Regel) nichts tun, wird möglicherweise keine Baugruppe für solche Destruktoren generiert.
Daniel Langr
2
@stark Siehe Was genau ist die "Als ob" -Regel? .
Daniel Langr
2
@stark Wo ist was definiert ? Beachten Sie, dass diese Diskussion nicht zum Thema der Frage gehört. Sie können eine andere separate Frage zu diesem Problem stellen.
Daniel Langr
8

Ja. Es ist einfacher zu visualisieren, wenn Sie die "Blöcke" betrachten, in denen Sie eine Variable deklarieren, dh zwischen welchen Klammern. Die Schleife ist ein Block für sich, und wenn sie vor der nächsten Iteration die schließende Klammer erreicht, werden alle Destruktoren der in der Schleife deklarierten automatischen Speichervariablen aufgerufen.

Könnte das Abrollen der Schleife durch den Compiler etwas daran ändern?

Als Faustregel sollten Sie nicht darüber nachdenken, was der Compiler optimieren wird, da er weiterhin das Verhalten Ihres Programms gewährleisten muss, unabhängig davon, was es zur Optimierung tut. In diesem Fall ändert das Abrollen der Schleife nichts in diesem Sinne, wenn dies geschieht.

JBL
quelle
2
+1 für die Faustregel: Wenn Sie Code schreiben, sollten Sie sich keine Gedanken über Compiler-Interna machen.
Ich wollte
Copy-Elision und RVO verändern das Programmverhalten, nicht wahr?
Jean-Baptiste Yunès
@ Jean-BaptisteYunès Sie können möglicherweise, aber der Standard erlaubt sie auch per[class.copy.elision]
ChrisMM
Nicht nur ein Paar Zahnspangen . Sie können schreiben for(...) X x{};und das xObjekt wird in jeder Iteration konstruiert + zerstört. Live-Demo . Ein relevanter Standardabschnitt ist stmt.iter / 2 .
Daniel Langr
@DanielsaysreinstateMonica Gemäß §9.5.2 ist [stmt.iter]es rein äquivalent (Hervorhebung von mir): "Wenn die Unteranweisung in einer Iterationsanweisung eine einzelne Anweisung und keine zusammengesetzte Anweisung ist, ist es so, als ob sie als zusammengesetzte Anweisung umgeschrieben wurde die ursprüngliche Aussage. ". Im Wesentlichen bedeutet mit oder ohne Klammern für eine einzelne Anweisung genau dasselbe, und die Klammern sind implizit. Ich habe es aus Gründen der Klarheit weggelassen.
JBL
2

Der Destruktor wird für jede Iteration aufgerufen. In einigen Fällen ist es daher schneller, eine Variable außerhalb der Schleife als in der Schleife zu deklarieren . Angenommen, der folgende Fall:

std::string temp;
for(int i = 0; i < 10; ++i){
    temp = arr[i];
    doSomething(temp);
}

Der Destruktor wird nicht aufgerufen, wenn die Verwendung der Schleife ausgeführt wird. Es überschreibt nur temp.

Wenn Sie jedoch std::string temp = arr[i]den Konstruktor verwenden, wird der Destruktor für jede Iteration aufgerufen. Ich denke, dies fügt ein bisschen Laufzeit hinzu, falls Sie eine Schleife haben, die sehr oft ausgeführt wird.

Julian Schnabel
quelle
Beachten Sie, dass Destruktoren, die aufgerufen werden oder nicht, nicht nur eine Frage der Leistung sind. Wenn Sie einen RAII-Typ haben, möchten Sie speziell, dass der Destruktor bei jeder Iteration aufgerufen wird
idclev 463035818
Ich bin mir nicht sicher, ob das stimmt. Wird der Destruktor des Inhalts 'temp' aus der vorherigen Iteration nicht sofort aufgerufen, wenn temp mit dem neuen Wert neu zugewiesen wird?
user1282931
Ich bin mir auch nicht 100% sicher. Korrigieren Sie meine Antwort, wenn Sie etwas falsch gefunden haben. :)
Julian Schnabel
0

Der Destruktor wird vor der nächsten Iteration aufgerufen

Girspoon
quelle
0

Natürlich wird dtor am Ende der Iteration aufgerufen, und das Abrollen der Schleife sollte dieses Verhalten nicht ändern, wie jede andere Optimierung (eine Optimierung sollte das Programmverhalten nicht ändern), außer einer Art RVO und dergleichen, die einige semantisch falsche Objekterstellungen eliminieren kann .

Jean-Baptiste Yunès
quelle