¿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 punteropydelete 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"?)

Respuestas a la pregunta(1)

Su respuesta a la pregunta