¿Por qué los deletreadores de rpp compartidos tienen que ser copyconstructibles?


En C++11 std::shared_ptr tiene cuatro constructores a los que se pueden pasar objetos deleter d de tipo D. Las firmas de estos constructores son las siguientes:

template<class Y, class D> shared_ptr(Y * p, D d);
template<class Y, class D, class A> shared_ptr(Y * p, D d, A a);
template <class D> shared_ptr(nullptr_t p, D d);
template <class D, class A> shared_ptr(nullptr_t p, D d, A a);

El estándar requiere en [útil.smartptr.compartir.const] escriba D para ser CopyConstructible. ¿Por qué es necesario? Si shared_ptr hace copias de d entonces, ¿cuál de estos deleters podría ser llamado? ¿No sería posible que un shared_ptr solo mantenga un solo deleter alrededor? ¿Qué significa para un shared_ptr a own un deleter si d se puede copiar?

¿Cuál es la razón detrás del requisito CopyConstructible?

PD: Este requisito podría complicar la escritura de deleters para shared_ptr. unique_ptr parece tener requisitos mucho mejores para su deleter.

Author: jotik, 2016-04-20

3 answers

Esta pregunta fue lo suficientemente desconcertante como para enviar un correo electrónico a Peter Dimov (implementador de boost::shared_ptr e involucrado en la estandarización de std::shared_ptr)

Aquí está la esencia de lo que dijo (reimpreso con su permiso):

Mi conjetura es que el Deleter tenía que ser CopyConstructible realmente solo como un reliquia de C++03 donde la semántica de movimiento no existía.

Tu suposición es correcta. Cuando se especificó shared_ptr rvalue referencias aún no existía. Hoy en día debe ser capaz de conseguir con requiriendo nothrow move-constructible.

Hay una sutileza en que cuando{[10]]}

pi_ = new sp_counted_impl_pd<P, D>(p, d);

Arrows, d debe quedar intacto para que la limpieza d(p) funcione, pero creo que esto no sería un problema (aunque en realidad no he trató de hacer que la implementación sea fácil de mover).
[...]
Creo que no habrá ningún problema para el implementación para definirlo de modo que cuando el new arroja, d quedará en su estado original.

Si vamos más allá y permitimos que D tenga un constructor de movimiento de lanzamiento, las cosas se complicar. Pero no lo haremos.: -)

 21
Author: AndyG,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2016-09-14 21:43:14

La diferencia entre deleters en std::shared_ptr y std::unique_ptr es que shared_ptr deleter es borrado de tipo, mientras que en unique_ptr deleter type es parte de la plantilla.

Aquí está Stephan T. Lavavej explicando cómo el borrado de tipos conduce a un requisito CopyConstructible en std::function.

En cuanto a la razón detrás de esta diferencia en los tipos de puntero, se ha abordado varias veces, por ejemplo, aquí.

Una cita de lo que S. T. L. dijo:

Muy sorprendente "gotcha" Yo diría que el std::function requiere objetos de función CopyConstructible, y esto es un poco inusual en el STL.

Por lo general, el STL es perezoso en el sentido de que no necesita cosas por adelantado: si tengo algo como un std::list de tipo T, T no necesita ser menos que comparable; solo si llama a la función miembro list<T>::sort entonces realmente necesita ser menos que comparable.

La regla básica del lenguaje que potencia esto es que las definiciones de las funciones miembro de una plantilla de clase no se instancian hasta que realmente se necesitan y los cuerpos no existen en algún sentido hasta que realmente lo llamas.

Esto suele ser genial - esto significa que solo pagas por lo que necesitas, pero std::function es especial debido al borrado de tipo, porque cuando construyes el std::function a partir de algún objeto llamable F necesita generar todo lo que puedas necesitar de ese objeto F porque va a borrar su tipo. Requiere todos los operaciones que posiblemente podría necesitar sin importar si se usan.

Así que si construyes un std::function a partir de algún objeto llamableF, F es absolutamente necesario en tiempo de compilación para ser CopyConstructible. Esto es cierto a pesar de que F se moverá a std::function, por lo que incluso si le da un valor r e incluso si nunca copia std::function s en cualquier lugar de su programa, F todavía se requiere que sea CopyConstructible.

Obtendrá un error del compilador que lo dice-tal vez horrible, tal vez agradable, dependiendo de lo que consigas.

Simplemente no puede almacenar objetos de función solo móviles. Esta es una limitación de diseño causada en parte por el hecho de que std::function se remonta a boost/TR1, antes de las referencias de valor r, y en algún sentido nunca se puede arreglar con la interfaz de std::function tal como está.

Se están investigando alternativas, tal vez podamos tener una "función móvil" diferente, por lo que probablemente obtendremos algún tipo de envoltura borrada que pueda almacenar única función en el futuro, pero std::function en c++17 ahora no puede hacer eso, así que acaba de ser conscientes.

 2
Author: Ap31,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-05-23 11:54:51

Porque shared_ptr están destinados a ser copiados, y cualquiera de esas copias podría tener que eliminar el objeto, por lo que todos deben tener acceso a un deleter. Mantener solo un deleter requeriría, bueno, volver a contar el deleter en sí. Si realmente quieres que eso suceda, podrías usar un std::shared_ptr anidado como deleter, pero eso suena un poco exagerado.

 -2
Author: mefyl,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2016-04-20 13:03:20