Howard ya ha respondido bien a la pregunta, y Nicol hizo algunos buenos comentarios sobre los beneficios de tener un solo tipo de puntero compartido estándar, en lugar de muchos incompatibles.
Si bien estoy completamente de acuerdo con la decisión del comité, creo que hay algún beneficio en el uso de un shared_ptr
tipo no sincronizado en casos especiales , por lo que he investigado el tema varias veces.
Si no estoy usando varios subprocesos, o si estoy usando varios subprocesos pero no comparto la propiedad del puntero entre subprocesos, un puntero inteligente atómico es excesivo.
Con GCC, cuando su programa no usa múltiples subprocesos, shared_ptr no usa operaciones atómicas para el refcount. Esto se hace actualizando los recuentos de referencias a través de funciones contenedoras que detectan si el programa es multiproceso (en GNU / Linux, esto se hace simplemente detectando si el programa se enlaza libpthread.so
) y se envían a operaciones atómicas o no atómicas en consecuencia.
Hace muchos años me di cuenta de que debido a que GCC shared_ptr<T>
se implementa en términos de una __shared_ptr<T, _LockPolicy>
clase base , es posible usar la clase base con la política de bloqueo de un solo subproceso incluso en código multiproceso, usando explícitamente __shared_ptr<T, __gnu_cxx::_S_single>
. Desafortunadamente, debido a que ese no era un caso de uso previsto, no funcionó de manera óptima antes de GCC 4.9, y algunas operaciones aún usaban las funciones de envoltura y, por lo tanto, se enviaban a operaciones atómicas a pesar de que solicitó explícitamente la _S_single
política. Véase el punto (2) en http://gcc.gnu.org/ml/libstdc++/2007-10/msg00180.htmlpara obtener más detalles y un parche para GCC para permitir que la implementación no atómica se use incluso en aplicaciones multiproceso. Me senté en ese parche durante años, pero finalmente lo comprometí para GCC 4.9, que le permite usar una plantilla de alias como esta para definir un tipo de puntero compartido que no es seguro para subprocesos, pero es un poco más rápido:
template<typename T>
using shared_ptr_unsynchronized = std::__shared_ptr<T, __gnu_cxx::_S_single>;
Este tipo no sería interoperable std::shared_ptr<T>
y solo sería seguro de usar cuando se garantiza que los shared_ptr_unsynchronized
objetos nunca se compartirán entre subprocesos sin una sincronización adicional proporcionada por el usuario.
Esto, por supuesto, es completamente no portátil, pero a veces está bien. Con los hacks de preprocesador adecuados, su código aún funcionaría bien con otras implementaciones si shared_ptr_unsynchronized<T>
es un alias shared_ptr<T>
, sería un poco más rápido con GCC.
Si está usando un GCC antes de 4.9, podría usarlo agregando las _Sp_counted_base<_S_single>
especializaciones explícitas a su propio código (y asegurándose de que nadie cree una instancia __shared_ptr<T, _S_single>
sin incluir las especializaciones, para evitar violaciones de ODR). Agregar tales especializaciones de std
tipos no está técnicamente definido, pero funciona en la práctica, porque en este caso no hay diferencia entre que yo agregue las especializaciones a GCC o usted las agregue a su propio código.