2013-06-05 7 views
19

Vor kurzem habe ich mit rvalues ​​"gespielt", um ihr Verhalten zu verstehen. Das meiste Ergebnis überraschte mich nicht, aber dann sah ich, dass, wenn ich eine lokale Variable werfe, der Move-Konstruktor aufgerufen wird.Warum werfen lokale Variablen ruft Moves Konstruktor?

Bis dahin dachte ich, dass der Zweck der Bewegungssemantikregeln darin besteht, zu garantieren, dass sich das Objekt nur dann bewegt (und ungültig wird), wenn der Compiler erkennt, dass es nicht mehr benutzt wird (wie in temporären Objekten) das Versprechen des Benutzers, es nicht zu benutzen (wie in std :: move).

Im folgenden Code wurde jedoch keine Bedingung beibehalten, und meine Variable wird immer noch verschoben (zumindest bei g ++ 4.7.3).

Warum ist das?

#include <iostream> 
#include <string> 
using namespace std; 

int main() { 
    string s="blabla"; 
    try { 
     throw s; 
    } 
    catch(...) { 
     cout<<"Exception!\n"; 
    } 
    cout<<s; //prints nothing 
} 
+3

wenn ja, sieht aus wie Fehler in gcc. –

+0

@kbok: Du hast Recht, ich habe meine Antwort gelöscht (also beantworte ich hier deinen Kommentar).Ich dachte irgendwie, 's' wäre im' try'-Block deklariert, hätte die Frage genauer lesen sollen. Sorry all –

+0

Bitte melden Sie es an http://gcc.gnu.org/bugzilla - danke! –

Antwort

5

Im vorliegenden Fall handelt es sich wahrscheinlich um einen Compiler-Fehler, da die Variable, auf die geworfen wird (und von der sie verschoben wird), später referenziert wird.

In der Regel Fall aufrufen Bewegung throw ist konzeptionell gleich wie weiter return. Es empfiehlt sich, die Verschiebung automatisch auszuführen, wenn bekannt ist, dass die Variable nach dem angegebenen Punkt nicht referenziert werden konnte (throw oder return).

+0

Danke. Es ist immer gut zu wissen, dass meine Intuition besser ist als gcc ... – asaelr

7

C++ Standard sagt (15.1.3):

eine Ausnahme copy-initialisiert Werfen (8.5, 12.8) ein temporäres Objekt, die Ausnahme-Objekt aufgerufen. Das Temporäre ist ein Lvalue und wird verwendet, um die Variable zu initialisieren, die in dem passenden Handler (15.3) genannt ist.

Dieser Absatz kann hier auch von Bedeutung sein (12.8.31):

Wenn bestimmte Kriterien erfüllt sind, eine Implementierung erlaubt ist, das Kopieren/Verschieben Bau eines Klassenobjekts zu verzichten, auch wenn der Konstruktor ausgewählt für die Kopie/Move-Operation und/oder den Destruktor für das Objekt haben Nebenwirkungen. In solchen Fällen behandelt die Implementierung die Quelle und das Ziel der ausgelassenen Kopier-/Verschiebungsoperation als einfach zwei unterschiedliche Arten des Verweises auf das gleiche Objekt, und die Zerstörung dieses Objekts tritt zu dem späteren Zeitpunkt der Zeit auf, wenn die zwei Objekte hätten ohne die Optimierung zerstört worden. Diese Auslassung von Kopieren/Verschieben-Operationen, Kopie elision genannt, wird in den folgenden Fällen zulässig (die mehrere Kopien zu eliminieren kombiniert werden kann):

(...)

- in einem Einwurf Ausdruck , wenn der Operand der Name eines nichtflüchtigen automatischen Objekts ist (außer einem Parameter function oder catch-clause) , dessen Gültigkeitsbereich nicht über das Ende des innersten umschließenden try-Blocks hinausgeht (falls vorhanden), Kopieren/Verschieben-Operation von dem Operanden zu dem Ausnahmeobjekt (15.1) kann weggelassen werden, indem das automatische Objekt direkt in das Ausnahmeobjekt
konstruiert wird

Karo in Visual Studio 2012, Wirkung:

Exception! 
blabla 

Es sieht aus wie ein Fehler in der Tat in GCC.

+0

Wenn ja, darf der Compiler den move-Konstruktor nicht aufrufen, auch wenn 'throw' nicht in einem' try'-Block ist? – asaelr

+0

@asaelr: Aus dem aktualisierten Zitat scheint es, dass das Objekt, das geworfen werden soll, kopiert, verschoben oder sogar an der richtigen Stelle konstruiert werden kann - genau wie für den Rückgabewert. –

+0

@asael Es ist. Aber in diesem speziellen Fall wird der move-ctor falsch aufgerufen, weil die Variable später außerhalb des try-Blocks verwendet wird. – Spook