Die Antwort von Herb (bevor sie bearbeitet wurde) gab tatsächlich ein gutes Beispiel für einen Typ, der nicht beweglich sein sollte : std::mutex
.
Der native Mutex-Typ des Betriebssystems (z. B. pthread_mutex_t
auf POSIX-Plattformen) ist möglicherweise nicht "standortinvariant", was bedeutet, dass die Adresse des Objekts Teil seines Werts ist. Beispielsweise kann das Betriebssystem eine Liste von Zeigern auf alle initialisierten Mutex-Objekte führen. Wenn std::mutex
ein Mutex-Typ des nativen Betriebssystems als Datenelement enthalten wäre und die Adresse des nativen Typs fest bleiben muss (da das Betriebssystem eine Liste von Zeigern auf seine Mutexe verwaltet), std::mutex
müsste entweder der native Mutex-Typ auf dem Heap gespeichert werden, damit er erhalten bleibt der gleiche Ort, wenn zwischen std::mutex
Objekten verschoben oder die std::mutex
nicht verschoben werden dürfen. Das Speichern auf dem Heap ist nicht möglich, da a std::mutex
einen constexpr
Konstruktor hat und für eine konstante Initialisierung (dh statische Initialisierung) geeignet sein muss, damit eine globalestd::mutex
wird garantiert erstellt, bevor die Programmausführung beginnt, sodass der Konstruktor sie nicht verwenden kann new
. Die einzige Möglichkeit ist std::mutex
also, unbeweglich zu sein.
Die gleiche Überlegung gilt für andere Typen, die etwas enthalten, für das eine feste Adresse erforderlich ist. Wenn die Adresse der Ressource unverändert bleiben muss, verschieben Sie sie nicht!
Es gibt ein weiteres Argument für std::mutex
das Nichtbewegen, nämlich, dass es sehr schwierig wäre, dies sicher zu tun, da Sie wissen müssen, dass niemand versucht, den Mutex zu sperren, sobald er verschoben wird. Da Mutexe einer der Bausteine sind, mit denen Sie Datenrennen verhindern können, wäre es bedauerlich, wenn sie nicht selbst gegen Rennen sicher wären! Mit einem Unbeweglichen std::mutex
wissen Sie, dass das einzige, was jeder daran tun kann, wenn es erstellt wurde und bevor es zerstört wurde, darin besteht, es zu sperren und zu entsperren. Diese Vorgänge sind ausdrücklich threadsicher und führen keine Datenrennen ein. Das gleiche Argument gilt für std::atomic<T>
Objekte: Wenn sie nicht atomar verschoben werden könnten, wäre es nicht möglich, sie sicher zu verschieben. Möglicherweise versucht ein anderer Thread, sie aufzurufencompare_exchange_strong
auf dem Objekt genau in dem Moment, in dem es bewegt wird. Ein weiterer Fall, in dem Typen nicht beweglich sein sollten, besteht darin, dass sie einfache Bausteine für sicheren gleichzeitigen Code sind und die Atomizität aller Operationen auf ihnen sicherstellen müssen. Wenn der Objektwert zu irgendeinem Zeitpunkt in ein neues Objekt verschoben werden kann, müssen Sie eine atomare Variable verwenden, um jede atomare Variable zu schützen, damit Sie wissen, ob die Verwendung sicher ist oder verschoben wurde ... und eine atomare Variable zum Schutz diese atomare Variable und so weiter ...
Ich denke, ich würde verallgemeinern, um zu sagen, dass es keinen Sinn macht, ein Objekt zu verschieben, wenn es nur ein reines Erinnerungsstück ist, kein Typ, der als Halter für einen Wert oder eine Abstraktion eines Wertes fungiert. Grundlegende Typen wie " int
Nicht verschieben": Das Verschieben ist nur eine Kopie. Sie können die Eingeweide nicht aus einem herausreißen int
, Sie können seinen Wert kopieren und dann auf Null setzen, aber es ist immer noch ein int
mit einem Wert, es sind nur Bytes Speicher. Aber ein int
ist noch beweglichin den Sprachbegriffen, weil eine Kopie eine gültige Verschiebungsoperation ist. Wenn Sie jedoch bei nicht kopierbaren Typen den Speicher nicht verschieben möchten oder können und auch seinen Wert nicht kopieren können, ist er nicht verschiebbar. Ein Mutex oder eine atomare Variable ist ein bestimmter Speicherort (der mit speziellen Eigenschaften behandelt wird), daher ist das Verschieben nicht sinnvoll und kann auch nicht kopiert werden, sodass er nicht verschoben werden kann.