He estado buscando el código fuente de Clang y encontré este fragmento:
void CompilerInstance::setInvocation(
std::shared_ptr<CompilerInvocation> Value) {
Invocation = std::move(Value);
}
¿Por qué querría std::moveun std::shared_ptr?
¿Hay algún punto para transferir la propiedad de un recurso compartido?
¿Por qué no haría esto en su lugar?
void CompilerInstance::setInvocation(
std::shared_ptr<CompilerInvocation> Value) {
Invocation = Value;
}
fuente

Al usarlo
move, evita aumentar y luego disminuir inmediatamente el número de acciones. Eso podría ahorrarle algunas operaciones atómicas costosas en el recuento de uso.fuente
Las operaciones de movimiento (como el constructor de movimientos)
std::shared_ptrson baratas , ya que básicamente son "robo de punteros" (de origen a destino; para ser más precisos, todo el bloque de control de estado es "robado" de origen a destino, incluida la información de recuento de referencia) .En cambio, las operaciones de copia en
std::shared_ptrinvocación aumentan el recuento de referencia atómica (es decir, no solo++RefCounten unRefCountmiembro de datos enteros , sino, por ejemplo,InterlockedIncrementen Windows), que es más costoso que simplemente robar punteros / estado.Entonces, analizando la dinámica de recuento de ref de este caso en detalle:
Si pasa
sppor valor y luego toma una copia dentro delCompilerInstance::setInvocationmétodo, tiene:shared_ptres un parámetro copia construidos: ref contar atómica de la subasta .shared_ptrparámetro en el miembro de datos: incremento atómico de recuento de referencia .shared_ptrparámetro se destruye: decremento atómico de recuento de ref .Tiene dos incrementos atómicos y un decremento atómico, para un total de tres operaciones atómicas .
En cambio, si pasa el
shared_ptrparámetro por valor y luegostd::movedentro del método (como se hace correctamente en el código de Clang), tiene:shared_ptres un parámetro copia construidos: ref contar atómica de la subasta .std::moveelshared_ptrparámetro en el miembro de datos: ¡el recuento de referencias no cambia! Solo está robando punteros / estado: no hay operaciones costosas de conteo de ref.shared_ptrparámetro se destruye; pero como te moviste en el paso 2, no hay nada que destruir, ya que elshared_ptrparámetro ya no apunta a nada. Una vez más, no se produce una disminución atómica en este caso.En pocas palabras: en este caso, obtienes solo un incremento atómico de recuento de referencia, es decir, solo una operación atómica .
Como puede ver, esto es mucho mejor que dos incrementos atómicos más un decremento atómico (para un total de tres operaciones atómicas) para el caso de copia.
fuente
compilerInstance.setInvocation(std::move(sp));no habrá incremento . Puede obtener el mismo comportamiento agregando una sobrecarga que requiere unshared_ptr<>&&pero por qué duplicar cuando no es necesario.setInvocation(new CompilerInvocation), o como se mencionó trinquete,setInvocation(std::move(sp)). Lo siento si mi primer comentario no fue claro, en realidad lo publiqué por accidente, antes de terminar de escribir, y decidí dejarloCopiar un
shared_ptrimplica copiar su puntero de objeto de estado interno y cambiar el recuento de referencia. Moverlo solo implica intercambiar punteros al contador de referencia interno y al objeto propio, por lo que es más rápido.fuente
Hay dos razones para usar std :: move en esta situación. La mayoría de las respuestas abordaron la cuestión de la velocidad, pero ignoraron la cuestión importante de mostrar la intención del código más claramente.
Para un std :: shared_ptr, std :: move denota inequívocamente una transferencia de propiedad del puntero, mientras que una simple operación de copia agrega un propietario adicional. Por supuesto, si el propietario original posteriormente renuncia a su propiedad (por ejemplo, al permitir que se destruya su std :: shared_ptr), se ha realizado una transferencia de propiedad.
Cuando transfiere la propiedad con std :: move, es obvio lo que está sucediendo. Si usa una copia normal, no es obvio que la operación prevista es una transferencia hasta que verifique que el propietario original renuncie inmediatamente a la propiedad. Como beneficio adicional, es posible una implementación más eficiente, ya que una transferencia atómica de propiedad puede evitar el estado temporal en el que el número de propietarios ha aumentado en uno (y los cambios correspondientes en los recuentos de referencia).
fuente
Al menos con libstdc ++ debería obtener el mismo rendimiento con mover y asignar porque
operator=llamastd::moveal puntero entrante. Ver: https://github.com/gcc-mirror/gcc/blob/master/libstdc%2B%2B-v3/include/bits/shared_ptr.h#L384fuente