¿El dtor de shared_ptr requiere el uso de un "eliminador"?
Sus extensamente conocido que puedes usar unshared_ptr
para almacenar un puntero a un tipo incompleto, siempre que el puntero pueda eliminarse (con un comportamiento bien definido) durante la construcción delshared_ptr
. Por ejemplo, la técnica PIMPL:
struct interface
{
interface(); // out-of-line definition required
~interface() = default; // public inline member, even if implicitly defined
void foo();
private:
struct impl; // incomplete type
std::shared_ptr<impl> pimpl; // pointer to incomplete type
};
[main.cpp]
int main()
{
interface i;
i.foo();
}
[interface.cpp]
struct interface::impl
{
void foo()
{
std::cout << "woof!\n";
}
};
interface::interface()
: pimpl( new impl ) // `delete impl` is well-formed at this point
{}
void interface::foo()
{
pimpl->foo();
}
Esto funciona como un"borrar objeto" El "objeto propietario" (*) se crea durante la construcción delshared_ptr
enpimpl( new impl )
, y almacenado después de borrar el tipo dentro de lashared_ptr
. Este "objeto propietario" se usa más tarde para destruir el objeto apuntado. Es por eso que debería ser seguro proporcionar unaen línea destructor deinterface
.
Pregunta: ¿Dónde garantiza el Estándar que es seguro?
(*) No es un borrado en términos de la Norma, ver más abajo, pero llama al borrado personalizado o invoca la expresión de eliminación. Este objeto normalmente se almacena como parte del objeto contable, aplicando el borrado de tipo e invocando la expresión / eliminación personalizada en una función virtual. En este punto, la expresión de eliminación debería estar bien formada también.
Refiriéndose al último borrador en el repositorio github (94c8fc71, revisando N3797), [util.smartptr.shared.const]
template<class Y> explicit shared_ptr(Y* p);
3 requiere:p
será convertible aT*
. Y
Será un tipo completo. La expresiondelete p
debe estar bien formado, debe tener un comportamiento bien definido y no debe lanzar excepciones.
4 efectos: construye unshared_ptr
objeto que posee el punterop
.
5 Postcondiciones:use_count() == 1 && get() == p
.
6 tiros:bad_alloc
, o una excepción definida por la implementación cuando no se pudo obtener un recurso que no sea la memoria.
Nota: Para este ctor,shared_ptr
no se requiere poseer un deleter. Poreliminar, el estándar parece significareliminación personalizada, como el que proporciona durante la construcción como un parámetro adicional (o elshared_ptr
adquiere / comparte una de otrashared_ptr
, p.ej. a través de copia-asignación). También vea (también vea [util.smartptr.shared.const] / 9). Las implementaciones (boost, libstdc ++, MSVC, y supongo que cada implementación sana) siempre almacenan un "objeto propietario".
Como uneliminar es uneliminación personalizada, el destructor deshared_ptr
se define en términos dedelete
(delete-expresión) si no hay un eliminador personalizado:
[util.smartptr.shared.dest]
~shared_ptr();
1 efectos:
Si*this
esvacío o comparte la propiedad con otroshared_ptr
instanciause_count() > 1
), No hay efectos secundarios.De lo contrario, si*this
posee un objetop
y un eliminadord
, d(p)
se llama.De otra manera,*this
posee un punterop
ydelete p
se llama.Asumiré elintención es que se requiere una implementación para eliminar correctamente el puntero almacenado incluso si está dentro del alcance de lashared_ptr
dtor, la expresión de eliminación está mal formada o invocaría UB. (La expresión de eliminación debe estar bien formada y tener un comportamiento bien definido en el ctor). Por lo tanto, la pregunta es
Pregunta: ¿Dónde se requiere esto?
(¿O es que soy demasiado delicado y es obvio que las implementaciones deben usar un "objeto propietario"?)