Frage

Wenn ich eine Klasse habe wie

class Foo{
public:
    Foo(){...}
    Foo(Foo && rhs){...}
    operator=(Foo rhs){ swap(*this, rhs);}
    void swap(Foo &rhs);
private:
    Foo(const Foo&);
// snip: swap code
};
void swap(Foo& lhs, Foo& rhs);

Ist es sinnvoll, Operator = nach Wert zu implementieren und auszutauschen, wenn ich keinen Kopierkonstruktor habe? Es sollte verhindern, dass meine Unterrichtsobjekte kopiert werden Foo aber umzüge zu.

Diese Klasse ist nicht kopierbar, daher sollte ich nicht in der Lage sein, das Konstrukt zu kopieren oder zuzuweisen.

Bearbeiten

Ich habe meinen Code damit getestet und es scheint das gewünschte Verhalten zu haben.

#include <utility>
#include <cstdlib>
using std::swap;
using std::move;
class Foo{
public: Foo():a(rand()),b(rand()) {}
        Foo(Foo && rhs):a(rhs.a), b(rhs.b){rhs.a=rhs.b=-1;}
        Foo& operator=(Foo rhs){swap(*this,rhs);return *this;}
        friend void swap(Foo& lhs, Foo& rhs){swap(lhs.a,rhs.a);swap(lhs.b,rhs.b);}
private:
    //My compiler doesn't yet implement deleted constructor
    Foo(const Foo&);
private:
    int a, b;
};

Foo make_foo()
{
    //This is potentially much more complicated
    return Foo();
}

int main(int, char*[])
{
    Foo f1;
    Foo f2 = make_foo(); //move-construct
    f1 = make_foo(); //move-assign
    f2 = move(f1);
    Foo f3(move(f2));
    f2 = f3; // fails, can't copy-assign, this is wanted
    Foo f4(f3); // fails can't copy-construct

    return 0;
}
War es hilfreich?

Lösung

Move-and-Swap ist in der Tat vernünftig. Wenn Sie den Kopierkonstruktor deaktivieren, können Sie diese Funktion nur aufrufen, wenn Sie das Argument mit dem Move Constructor konstruieren. Das bedeutet, dass wenn Sie schreiben, wenn Sie schreiben

lhs = rhs; // Assume rhs is an rvalue

Dann der Konstruktor des Arguments an operator = wird mit dem Move Constructor initialisiert und entleert rhs und das Argument auf den alten Wert von festlegen rhs. Der Anruf an swap dann austauscht lhsalter Wert und rhsalter Wert, gehen lhs Holding rhsalter Wert. Schließlich feuert der Destruktor für das Argument zusammen und räumen Sie auf lhsalte Erinnerung. Als Notiz ist das wirklich nicht Kopieren-and swap so viel wie Bewegung-und-Swap.

Das heißt, was Sie jetzt haben, ist nicht richtig. Die Standardimplementierung von std::swap Wird intern versuchen, den Move Constructor zu verwenden, um die Elemente umzuziehen, was zu einer unendlichen Rekursionsschleife führt. Sie müssten überlasten std::swap Damit dies richtig funktioniert.

Sie können dies online sehen hier bei ideone.

Weitere Informationen finden Sie unter diese Frage und seine Diskussion über die "Regel von viereinhalb".

Hoffe das hilft!

Andere Tipps

Ich denke, das ist in Ordnung, aber ich verstehe nicht wirklich, warum Sie es nicht einfach tun würden:

operator=(Foo&& rhs) // pass by rvalue reference not value

Und sparen Sie sich einen Schritt.

Was folgt, ist die Meinung, und ich bin nicht wirklich auf dem 0x -Standard, aber ich denke, ich habe ziemlich solide Argumentation, die mich unterstützt.

Nein. Tatsächlich wäre es richtig, die Zuordnung überhaupt nicht zu unterstützen.

Betrachten Sie die Semantik:

"Zuweisung" bedeutet "Ursache B, was bereits existiert, um mit A identisch zu sein". "Kopie" bedeutet "Erstellen Sie B und lassen Sie es identisch mit a". "Swap" bedeutet "Ursache B ist identisch mit dem, was A war, und gleichzeitig dazu führen, dass a identisch mit dem ist, was B war". "Bewegung" bedeutet "Ursache B ist identisch mit dem, was A war, und zerstören A."

Wenn wir nicht kopieren können, können wir nicht kopieren und schweigen. Copy-and-Swap soll eine sichere Möglichkeit zur Implementierung der Zuordnung sein: Wir erstellen C, was mit A identisch ist, mit B tauschen (so dass C jetzt das ist, was B war, und B identisch mit A) und C zerstören C. (Reinigen Sie die alten B -Daten). Dies funktioniert einfach nicht mit Move-and-Swap: Wir dürfen zu keinem Zeitpunkt eine zerstören, aber der Umzug wird es zerstören. Außerdem schafft das Bewegung keinen neuen Wert. Was passiert, ist also, dass wir A in B bewegen, und dann gibt es nichts zu tauschen.

Außerdem ist der Grund, dass die Klasse nicht kopierbar ist, sicherlich nicht, weil "erstellen B" problematisch sein wird, sondern weil "sie identisch mit a" problematisch ist. Iow, wenn wir nicht kopieren können, warum sollten wir dann erwarten, dass wir zuweisen können?

Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top