2016-09-21 8 views
2

Schauen wir uns einige triviale aussehen bewegen-konstruierbar und (nicht triviale) copy-konstruierbar (aber immer noch Copy- konstruierbar) benutzerdefinierte (Klasse) Typ A:Unterscheidet sich das einfache Kopieren und Verschieben?

struct A 
{ 
    A() = default; 
    A(A const &) {} 
    A(A &&) = default; 
}; 

Dann A bewegen (move-Konstruktion oder move-assignment) führt buchstäblich folgendes durch: Eine Quelle bitweise kopierte an ein Ziel, trotz des Namens der Operation "moving". Während trivial bewegende rechte Seite ist (formal) nicht const, aber Trivialität der ganzen Operation erfordert (tatsächliche) Nicht-Veränderlichkeit der rechten Seite, nicht wahr? In meinen Augen bedeutet dies, dass triviale Kopieroperationen und triviale Verschiebeoperationen in ihrer tiefen Natur (in Bezug auf Speicher, Speicherlayout, Bits usw.) genau gleich sind. Habe ich recht?

Wenn es so ist, dann denke ich, wenn ich trivial move-constructible, aber nicht trivial Copy-Constructible geben Benutzercode ein, dann sehe ich offensichtlich einige Antipattern. Habe ich recht?

Gibt es ein Beispiel für einen solchen künstlichen aber verwendbaren Typ, der nicht trivial kopierbar/zuweisbar ist, sondern trivial move-constructible/assignable?

+3

"* In meinen Augen bedeutet dies, dass triviale Kopieroperation und triviale Bewegungsoperation in ihrer tiefen Natur (bezüglich Speicher, Speicherlayout, Bits usw.) genau gleich sind. Habe ich recht? *" Ja . – ildjarn

+0

Äh, diese Frage ist überhaupt nicht mit dieser Frage verbunden. – Barry

+2

Fall "Edge": Ein Typ, der nicht copy-constructible ist, kann trivial move-constructible und nicht trivial copy-constructible sein (ein Konstruktor für gelöschte Kopien ist (offensichtlich) nicht trivial). – Holt

Antwort

2

Gibt es einen Anwendungsfall, in dem ein Typ einen trivialen Kopierkonstruktor haben kann, ohne einen trivialen Move-Konstruktor zu haben? Sicher.

Zum Beispiel könnte es nützlich sein, einen Zeiger-Wrapper-Typ zu haben, der immer leer ist, wenn er von bewegt wird. Es gibt keinen Grund dafür, dass der Kopierkonstruktor nicht-trivial ist, aber der Move-Konstruktor müsste den alten Wert auf NULL setzen.

template<typename T> 
class empty_on_move 
{ 
    T *ptr_; 

public: 
    empty_on_move(const empty_on_move&) = default; 
    empty_on_move(empty_on_move &&other) : ptr_(other.ptr_) {other.ptr_ = nullptr;} 
... 
}; 

empty_on_move besitzt das Objekt nicht, weshalb es OK ist, mehrere Kopien davon zu haben. Es existiert nur, um sicherzustellen, dass der Zeiger in einem gut verstandenen Zustand ist, wenn Sie sich von ihm entfernen. Als solches ist is_trivially_copy_constructible<empty_on_move<T>> wahr, während is_trivially_move_constructible<empty_on_move<T>> falsch ist.

Es wäre hauptsächlich für den Einsatz innerhalb anderer Klassen, die Zeiger geben wollen, dass bestimmtes Verhalten. Auf diese Weise müssen Sie nicht explizit Code in ihre Move-Konstruktoren/Zuweisungen schreiben, um diese Felder zu NULL zu machen.


Das gesagt, Sie fragen wirklich die falsche Frage. Warum? Weil die Antwort keine Rolle spielt.

Das einzige Mal, dass die Trivialität eines Copy/Move Konstruktors/Zuweisung wichtig ist, wenn Sie den Typ Trivially Copyable benötigen. Es ist dass Eigenschaft, die die Verwendung von memcpy und solche Dinge erlaubt, nicht die trivialen der einzelnen Operationen. Die trivial kopierbare Eigenschaft erfordert, dass der Konstruktor und die Destruktoren copy/move alle trivial (oder gelöscht, ab C++ 14) sind.

Wenn Sie einen Wrapper für einen bestimmten Typ schreiben (oder eine Summe/einen Produkttyp schreiben) und die Eigenschaften dieses Typs verfügbar machen möchten, müssen Sie sich nur mit Trivial Copyability beschäftigen. Das heißt, wenn T (oder) trivial kopierbar ist, sollte Ihr Typ auch trivial kopierbar sein.

Aber sonst sollten Sie nicht das Bedürfnis haben, einen trivialen Kopierkonstruktor zu haben, nur weil T tut.

+0

Warum ein trivial kopierbarer Typ sollte einen trivialen Destruktor haben? Logischerweise ist es überhaupt nicht erforderlich. – Orient

+1

@Orient: Ein Typ, für den alle diese Operationen trivial sind, ist ein Typ, der, was das C++ - Objektmodell betrifft, keinen Zustand hat, der über den Wert seiner Bits hinausgeht. Daher ist das bitweise Kopieren auf einen beliebigen Speicherplatz eine sinnvolle Maßnahme. Wenn ein Destruktor nicht-trivial ist, dann kann der Destruktor etwas tun, das das Objekt mehr als nur einen Block von Bits macht. Bitweises Kopieren ist daher keine sinnvolle Maßnahme mehr. Betrachte 'lock_guard'; es hat Kopier-/Verschiebungskonstruktoren/-zuweisungen gelöscht. Aber es hat einen nicht-trivialen Destruktor. Soll man es bitweise kopieren? *Natürlich nicht*. –

+0

Das Verbot, 'lock_guard' zu kopieren, wird in gelöschten Kopier-/Verschiebungskonstruktoren/Zuweisungsoperatoren klar ausgedrückt. Trivialität des Destruktors ist nicht verwandt. '! std :: is_copy_constructible' impy'! std :: is_trivial_copy_constructible'. – Orient

Verwandte Themen