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_ptr
con é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_ptr
como 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::function
en 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);
sicustomdeleter
sigue 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
BarDeleter
que 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_ptr
constructor 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> UniqueBarPtr
unique_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 eltypedef
proveedor 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_ptr
objeto 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_fn
es lo que es.Simplemente puede usar
std::bind
con 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
::std
siempre 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
std
abre una nueva lata de gusanos. También tenga en cuenta que la especialización destd::make_unique
no 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 lasstd
que no hay plantillas de clase (std::make_unique
es 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_delete
cumple 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, siFoo
está completo), este no sería el mismo comportamiento. Además, sidelete p;
no fue válido escribir (Foo
está incompleto), esto estaría especificando un nuevo comportamiento parastd::default_delete<Foo>
, en lugar de mantener el mismo comportamiento.make_unique
especialización es problemática, pero definitivamente he usado lastd::default_delete
sobrecarga (no con plantillaenable_if
, solo para estructuras C como OpenSSLBIGNUM
que 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
/using
para dar un nombre a dicho tipo para evitar ese problema.