2017-12-15 5 views
4

Der folgende Code führt zu SIGSEGV und ich kann nicht verstehen, warum das ist.Warum passiert std :: move (Objekt) und Mitglied dieses Objekts zu funktionieren SIGSEGV

#include <iostream> 
using namespace std; 

struct C { 
    C(int x) { ptr = new int(x); } 
    C(C&& c) { ptr = c.ptr; c.ptr = nullptr; } 

    int* ptr; 
}; 

void foo(int* x, C c) { 
    cout << *x << endl; 
} 

int main() { 
    C c(10); 
    foo(c.ptr, std::move(c)); 
    return 0; 
} 

Ich würde erwarten, dass der Zeiger c.ptr von Wert an die Funktion foo übergeben werden, aber es ist durch Bezugnahme wie seine vergangen verhält.

Wenn ich jetzt die Reihenfolge der Argumente ändern: void foo (C c, int * x), dann verschwindet das Problem. Die andere Lösung besteht darin, eine lokale Kopie von c.ptr vor dem Aufruf von x zu erstellen und diese lokale Kopie an foo weiterzuleiten.

Ich würde gerne verstehen, warum ich c.ptr nach Wert im obigen Beispielcode nicht übergeben kann.

+0

Sie bewegten sich zu einem temporären ... – user1810087

Antwort

6

Es wird Wert übergeben, aber:

foo(c.ptr, std::move(c)); 

es in welcher Reihenfolge die Parameter that are passed to a function call get evaluated unspezifiziert ist.

Auftrag der Auswertung der Operanden von fast allen C++ Operatoren (einschließlich der Reihenfolge der Auswertung von Funktionsargumenten in einem Funktionsaufruf Ausdruck ...) ist nicht spezifiziert.

"Nicht spezifiziert" bedeutet, dass sie in beliebiger Reihenfolge ausgewertet werden können. Die Reihenfolge kann bei jedem Start des Programms sogar anders sein. Ihr Compiler hat sich entschieden, Code zu generieren, der den 2. Parameter zuerst mit std::move auswertet. Daher verschiebt Ihr Move-Konstruktor den Zeiger aus dem Objekt und setzt ihn auf null. Dann wird c.ptr ausgewertet, wobei der NULL-Zeiger nach Wert übergeben wird.

+0

Gibt es eine Möglichkeit, die Auswertung des ersten Arguments vor der Verschiebung zu erzwingen, die nicht auf Compiler-Implementierungsdetails angewiesen wäre, ohne ein temporäres Objekt zu erstellen? – Buyuk

+1

@Buyuk Sie können es selbst auswerten und das Ergebnis speichern. 'int * x = c.ptr; foo (x, std :: move (c)); ' –

+0

@ FrançoisAndrieux Ich weiß, aber ich frage mich, ob es eine Möglichkeit gibt, es zu tun, ohne temporäre Objekte (in diesem Fall Zeiger) zu erstellen, weil es manchmal teuer sein kann. – Buyuk

3

Wenn Sie foo(c.ptr, std::move(c)); anrufen können Sie nicht sicher sein, eher c.ptr oder std::move(c) wird zuerst ausgewertet werden. Die Bestellargumente werden in einem Funktionsaufruf ausgewertet, der nicht spezifiziert ist. Es scheint in Ihrem Fall, dass std::move(c) zuerst ausgewertet wird, c.ptr als nullptr verlassen. Sie tun dann cout << *x << endl, die versucht, *x dereferenzieren, wobei xnullptr ist.

Verwandte Themen