Ich habe den Clang-Quellcode durchgesehen und diesen Ausschnitt gefunden:
void CompilerInstance::setInvocation(
std::shared_ptr<CompilerInvocation> Value) {
Invocation = std::move(Value);
}
Warum sollte ich will std::move
ein std::shared_ptr
?
Gibt es einen Grund, das Eigentum an einer gemeinsam genutzten Ressource zu übertragen?
Warum sollte ich das nicht einfach stattdessen tun?
void CompilerInstance::setInvocation(
std::shared_ptr<CompilerInvocation> Value) {
Invocation = Value;
}
quelle
Durch die Verwendung
move
vermeiden Sie, die Anzahl der Freigaben zu erhöhen und dann sofort zu verringern. Das könnte Ihnen einige teure atomare Operationen bei der Anzahl der Nutzungen ersparen.quelle
Verschiebungsoperationen (wie der Verschiebungskonstruktor) für
std::shared_ptr
sind billig , da sie im Grunde genommen "Zeiger stehlen" (von Quelle zu Ziel; genauer gesagt, der gesamte Statussteuerblock wird von Quelle zu Ziel "gestohlen", einschließlich der Referenzzählinformationen). .Stattdessen erhöhen sich die Kopiervorgänge beim
std::shared_ptr
Aufrufen der Anzahl der Atomreferenzen (dh nicht nur++RefCount
bei einem ganzzahligenRefCount
Datenelement, sondern z. B. beim AufrufenInterlockedIncrement
von Windows), was teurer ist als nur das Stehlen von Zeigern / Status.Analyse der Ref-Count-Dynamik dieses Falls im Detail:
Wenn Sie einen
sp
Wert übergeben und dann eine Kopie innerhalb derCompilerInstance::setInvocation
Methode erstellen, haben Sie:shared_ptr
Parameter kopierkonstruiert: ref count atomares Inkrement .shared_ptr
Parameter in das Datenelement: ref count atomares Inkrement .shared_ptr
Parameter zerstört: ref count atomares Dekrement .Sie haben zwei atomare Inkremente und ein atomares Dekrement für insgesamt drei atomare Operationen.
Wenn Sie stattdessen den
shared_ptr
Parameter als Wert und dannstd::move
innerhalb der Methode übergeben (wie in Clangs Code korrekt ausgeführt), haben Sie:shared_ptr
Parameter kopierkonstruiert: ref count atomares Inkrement .std::move
denshared_ptr
Parameter in das Datenelement ein: ref count ändert sich nicht ! Sie stehlen nur Zeiger / Status: Es sind keine teuren atomaren Ref-Count-Operationen erforderlich.shared_ptr
Parameter zerstört. Aber seit Sie in Schritt 2 umgezogen sind, gibt es nichts zu zerstören, da dershared_ptr
Parameter auf nichts mehr zeigt. Auch in diesem Fall findet keine atomare Dekrementierung statt.Fazit: In diesem Fall erhalten Sie nur ein Atominkrement für die Ref-Anzahl, dh nur eine Atomoperation .
Wie Sie sehen können, ist dies viel besser als zwei atomare Inkremente plus ein atomares Dekrement (für insgesamt drei atomare Operationen) für den Kopierfall.
quelle
compilerInstance.setInvocation(std::move(sp));
gibt es kein Inkrement . Sie können das gleiche Verhalten erzielen, indem Sie eine Überladung hinzufügen, die eine,shared_ptr<>&&
aber warum duplizieren, wenn Sie nicht müssen.setInvocation(new CompilerInvocation)
oder wie erwähntsetInvocation(std::move(sp))
. Es tut mir leid, wenn mein erster Kommentar unklar war. Ich habe ihn tatsächlich versehentlich gepostet, bevor ich mit dem Schreiben fertig war, und ich habe beschlossen, ihn einfach zu verlassenBeim Kopieren von a wird
shared_ptr
der interne Statusobjektzeiger kopiert und der Referenzzähler geändert. Beim Verschieben werden nur Zeiger auf den internen Referenzzähler und das eigene Objekt ausgetauscht, sodass es schneller geht.quelle
In dieser Situation gibt es zwei Gründe für die Verwendung von std :: move. Die meisten Antworten befassten sich mit dem Thema Geschwindigkeit, ignorierten jedoch das wichtige Problem, die Absicht des Codes klarer darzustellen.
Bei einem std :: shared_ptr bezeichnet std :: move eindeutig eine Eigentumsübertragung des Pointees, während ein einfacher Kopiervorgang einen zusätzlichen Eigentümer hinzufügt. Wenn der ursprüngliche Eigentümer später sein Eigentum aufgibt (z. B. indem er zulässt, dass sein std :: shared_ptr zerstört wird), wurde natürlich eine Eigentumsübertragung durchgeführt.
Wenn Sie das Eigentum mit std :: move übertragen, ist es offensichtlich, was passiert. Wenn Sie eine normale Kopie verwenden, ist es nicht offensichtlich, dass es sich bei dem beabsichtigten Vorgang um eine Übertragung handelt, bis Sie überprüfen, ob der ursprüngliche Eigentümer das Eigentum sofort aufgibt. Als Bonus ist eine effizientere Implementierung möglich, da durch eine atomare Eigentumsübertragung der vorübergehende Zustand vermieden werden kann, in dem sich die Anzahl der Eigentümer um eins erhöht hat (und die damit verbundenen Änderungen der Referenzzählungen).
quelle
Mindestens mit libstdc ++ sollten Sie die gleiche Leistung mit Bewegung und Zuordnung , da bekommen
operator=
Anrufestd::move
auf dem eingehenden Zeiger. Siehe: https://github.com/gcc-mirror/gcc/blob/master/libstdc%2B%2B-v3/include/bits/shared_ptr.h#L384quelle