¿El comportamiento estándar de los deleters difiere entre el rpp compartido y el rpp único en el caso de los punteros nulos?


OK, así que primero algunas cosas que podrían ser relevantes:

Estoy usando el compilador Clang 3.1, en modo C++11, con la biblioteca estándar establecida en libc++.

Estoy tratando de familiarizarme con C++11, y al hacerlo me encontré con un comportamiento que parece extraño. Puede ser una peculiaridad de Clang o libc++ , pero no puedo hablar C++ standardese y no tengo acceso a otros compiladores con soporte para C++11, así que realmente no puedo comprobarlo, y he buscado en Internet y Desbordamiento de pila al mejor de mis habilidad sin encontrar nada related...so aquí vamos:

Cuando se usa shared_ptr / unique_ptr para implementar RAII para un recurso simple, parece que su comportamiento difiere con respecto a los punteros nulos al eliminarlos. Me doy cuenta de que normalmente no es necesario eliminar un puntero nulo, pero esperaba que el comportamiento al menos coincidiera entre los dos punteros inteligentes STL.

Para el caso específico, considere el siguiente código:

{
    auto Deleter = [](void *){cout << "It's later!" << endl;};
    shared_ptr<void> spDoSomethingLater(nullptr, Deleter);
    unique_ptr<void, void (*)(void *)> upDoSomethingLater(nullptr, Deleter);
    cout << "It's now!" << endl;
}

Habría esperado uno de los los siguientes resultados:

A) Si se llama a ambos deleters aunque el puntero sea nulo:

"It's now!"
"It's later!"
"It's later!"

B) Si no se llama a ninguno de los deleter porque el puntero es nulo:

"It's now!"

Pero no observo ninguno de estos casos. En cambio, observo:

"It's now!"
"It's later!"

Lo que significa que uno pero no el otro de los deleters está siendo llamado. Tras una investigación adicional, descubrí que el deleter para shared_ptr se llama independientemente de si tiene un valor nulo, pero el deleter de unique_ptr es solo se llama si no contiene un valor null.

Mis preguntas: ¿Es éste realmente el comportamiento correcto según lo especificado por el estándar? Si es así, ¿por qué el comportamiento especificado difiere entre los dos tipos STL de esta manera? Si no es así, ¿es un error que debería reportar a libc++?

Author: R. Martinho Fernandes, 2012-06-23

2 answers

El comportamiento observado está de acuerdo con el estándar.

Para unique_ptr, 20.7.1.2.2/2 (efectos destructor) dice

Efectos: Si get() == nullptr no hay efectos. De lo contrario get_deleter()(get()).

Para shared_ptr, 20.7.2.2.2 / 1 dice que el deleter debe ser llamado incluso si envuelve el puntero nulo:

Efectos:

  • Si * esto está vacío o comparte la propiedad con otro shared_ptr instancia (use_count() > 1), no hay efectos secundarios.
  • De lo contrario, si * posee un objeto p y un deleter d, d(p) se llama.
  • De lo contrario, *posee un puntero p, y delete p es llamado.

El detalle importante aquí es la expresión "posee un objeto p". 20.7.2.2 / 1 dice que "un objeto shared_ptr es vacío si no posee un puntero". 20.7.2.2.1 / 9 (el constructor relevante) dice que " construye un objeto shared_ptr que posee el objeto p y el deleter d".

Por lo que puedo decir, esa invocación técnicamente hace que la shared_ptr own the null pointer, which results in the deleter being called. Contraste esto con el constructor sin parámetros que se dice que deja el shared_ptr "empty ".

 25
Author: Jon,
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
2012-06-22 22:07:25

Sí es el comportamiento correcto.

§20.7.1.2.2 [único.ptr.solo.dtor] / 2:

unique_ptr destructor

Efectos : Si get() == nullptr no hay efectos. De lo contrario get_deleter()(get()).

§20.7.2.2.2 [útil.smartptr.compartir.dest] / 1:

shared_ptr destructor

Efectos:

  • Si *this está vacío o comparte la propiedad con otra instancia shared_ptr (use_count() > 1), no hay lado effects.
  • de lo Contrario, si *this posee un objeto p y un deleter d, d(p) se llama.
  • De lo contrario, *this posee un puntero p, y se llama delete p.

Así que no debería haber ningún efecto ya que el shared_ptr está vacío? Incorrecto, porque proporcionar el puntero a hace que shared_ptr no esté vacío, incluso si el puntero es nulo.

§20.7.2.2.1 [útil.smartptr.compartir.const] / 8-10

shared_ptr constructores

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);

Requiere: p será convertible a T*. …

Efectos: Construye un shared_ptr objeto que posee el objeto p y el deleter d.

Postcondiciones: use_count() == 1 && get() == p.

Esto significa que el shared_ptr posee el nullptr. Así el deleter será llamado cuando el shared_ptr sea destruido.

 9
Author: kennytm,
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
2012-06-22 21:41:14