¿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++?
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 contrarioget_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 deleterd
,d(p)
se llama.- De lo contrario, *posee un puntero
p
, ydelete 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 ".
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
destructorEfectos : Si
get() == nullptr
no hay efectos. De lo contrarioget_deleter()(get())
.
§20.7.2.2.2 [útil.smartptr.compartir.dest] / 1:
shared_ptr
destructorEfectos:
- Si
*this
está vacío o comparte la propiedad con otra instanciashared_ptr
(use_count() > 1
), no hay lado effects.- de lo Contrario, si
*this
posee un objetop
y un deleterd
,d(p)
se llama.- De lo contrario,
*this
posee un punterop
, y se llama deletep
.
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
constructorestemplate<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 aT*
. …Efectos: Construye un
shared_ptr
objeto que posee el objetop
y el deleterd
.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.
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