Tengo una clase con un miembro unique_ptr.
class Foo {
private:
std::unique_ptr<Bar> bar;
...
};
The Bar es una clase de terceros que tiene una función create () y una función destroy ().
Si quisiera usar un std::unique_ptrcon él en una función independiente, podría hacer:
void foo() {
std::unique_ptr<Bar, void(*)(Bar*)> bar(create(), [](Bar* b){ destroy(b); });
...
}
¿Hay alguna manera de hacer esto std::unique_ptrcomo miembro de una clase?
c++
c++11
move-semantics
unique-ptr
huitlarc
fuente
fuente

std::unique_ptr<Bar, decltype(&destroy)> ptr_;unique_ptr(todos deben almacenar el puntero de la función junto con el puntero a los datos reales), requiere pasar la función de destrucción cada vez, no puede estar en línea (ya que la plantilla no puede se especializa en la función específica, solo la firma), y debe llamar a la función a través del puntero (más costoso que la llamada directa). Tanto las respuestas de rici como las de Deduplicator evitan todos estos costos al especializarse en un functor.Es posible hacer esto limpiamente usando un lambda en C ++ 11 (probado en G ++ 4.8.2).
Dado esto reutilizable
typedef:Puedes escribir:
Por ejemplo, con un
FILE*:Con esto, obtiene los beneficios de una limpieza segura con excepción utilizando RAII, sin necesidad de intentar / detectar ruido.
fuente
std::functionen la definición o algo así?std::function. Lambda o clase personalizada como en la respuesta aceptada se pueden incluir a diferencia de esta solución. Pero este enfoque tiene ventaja en caso de que desee aislar toda la implementación en un módulo dedicado.deleted_unique_ptr<Foo> foo(new Foo(), customdeleter);sicustomdeletersigue la convención (devuelve vacío y acepta un puntero sin formato como argumento).Solo necesita crear una clase de eliminación:
y proporcionarlo como argumento de plantilla de
unique_ptr. Aún tendrá que inicializar el unique_ptr en sus constructores:Hasta donde yo sé, todas las bibliotecas populares de c ++ implementan esto correctamente; dado
BarDeleterque en realidad no tiene ningún estado, no necesita ocupar ningún espacio en elunique_ptr.fuente
struct BarDeleter) astd::unique_ptr(std::unique_ptr<Bar, BarDeleter>) que permite alstd::unique_ptrconstructor crear una instancia Deleter por sí mismo. es decir, se permite el siguiente códigostd::unique_ptr<Bar, BarDeleter> bar[10];typedef std::unique_ptr<Bar, BarDeleter> UniqueBarPtrunique_ptr, no es necesario proporcionar una instancia del eliminador al construir), y agrega el beneficio de poder usar enstd::unique_ptr<Bar>cualquier lugar sin necesidad de recordar para usar eltypedefproveedor especial o explícito el segundo parámetro de plantilla. (Para ser claros, esta es una buena solución, voté a favor, pero se detiene un paso porA menos que necesite poder cambiar el eliminador en tiempo de ejecución, le recomiendo utilizar un tipo de eliminador personalizado. Por ejemplo, si utiliza un puntero de función para su Deleter,
sizeof(unique_ptr<T, fptr>) == 2 * sizeof(T*). En otras palabras, la mitad de los bytes delunique_ptrobjeto se desperdician.Sin embargo, escribir un eliminador personalizado para ajustar cada función es una molestia. Afortunadamente, podemos escribir un tipo con plantilla en la función:
Desde C ++ 17:
Antes de C ++ 17:
fuente
deleter_from_fnes lo que es.Simplemente puede usar
std::bindcon una función de destrucción.Pero, por supuesto, también puedes usar una lambda.
fuente
Ya sabes, usar un eliminador personalizado no es la mejor manera de hacerlo, ya que tendrás que mencionarlo en todo tu código.
En cambio, como puede agregar especializaciones a las clases de nivel de espacio de nombres
::stdsiempre que estén involucrados tipos personalizados y respete la semántica, haga lo siguiente:especializarse
std::default_delete:Y tal vez también lo haga
std::make_unique():fuente
stdabre una nueva lata de gusanos. También tenga en cuenta que la especialización destd::make_uniqueno está permitida después de C ++ 20 (por lo tanto, no debería hacerse antes) porque C ++ 20 no permite la especialización de cosas en lasstdque no hay plantillas de clase (std::make_uniquees una plantilla de función). Tenga en cuenta que usted también probablemente terminará con UB cuando el puntero pasa alstd::unique_ptr<Bar>no fueron asignados delcreate(), sino de alguna otra función de asignación.std::default_deletecumple con el requisito de la plantilla original. Me imagino questd::default_delete<Foo>()(p)sería una forma válida de escribirdelete p;, por lo que sidelete p;fuera válido para escribir (es decir, siFooestá completo), este no sería el mismo comportamiento. Además, sidelete p;no fue válido escribir (Fooestá incompleto), esto estaría especificando un nuevo comportamiento parastd::default_delete<Foo>, en lugar de mantener el mismo comportamiento.make_uniqueespecialización es problemática, pero definitivamente he usado lastd::default_deletesobrecarga (no con plantillaenable_if, solo para estructuras C como OpenSSLBIGNUMque usan una función de destrucción conocida, donde la subclasificación no va a suceder), y es, con mucho, el enfoque más fácil, ya que el resto de su código puede usarlounique_ptr<special_type>sin necesidad de pasar el tipo de functor como el que está en plantillaDeleter, ni usartypedef/usingpara dar un nombre a dicho tipo para evitar ese problema.