Ist es sinn?
-
26-10-2019 - |
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;
}
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 lhs
alter Wert und rhs
alter Wert, gehen lhs
Holding rhs
alter Wert. Schließlich feuert der Destruktor für das Argument zusammen und räumen Sie auf lhs
alte 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?