smart shared_ptr make_unique c++ lambda c++11 smart-pointers unique-ptr

c++ - shared_ptr - unique_ptr<T> lambda custom deleter para la especialización de arreglos



shared_ptr c++ (2)

Esta pregunta ya tiene una respuesta aquí:

Recientemente comencé a portar muchos de mis códigos de aplicaciones C ++ existentes a C ++ 11 y ahora que me estoy convirtiendo a los nuevos punteros inteligentes std :: unique_ptr y std :: shared_ptr , tengo una pregunta específica sobre los modificadores personalizados. Quiero agregar un registrador lambda para ver dónde se están llamando mis eliminaciones, pero no puedo obtener la versión de especialización de la matriz para compilar. El consejo sería muy apreciado.

He estado buscando en vano un ejemplo de un eliminador personalizado para la especialización de arreglos unique_ptr para VC ++ 10 o GCC 4.5.2+ . Me gustaría imprimir un mensaje de registro cuando se llaman los deleters en un lambda, principalmente para asegurarme de que todos los punteros que creo que están saliendo del alcance lo están haciendo. ¿Es esto posible para la versión de matriz de la especialización? Puedo hacer que funcione con la versión que no es de matriz, y también puedo hacer que funcione con una especialización de matriz si paso una estructura externa "MyArrayDeleter" como segundo argumento. Una cosa más, sería posible eliminar la función fea std :: ya que pensé que podía dejar que la firma lambda lo descubriera.

struct MySimpleDeleter { void operator()(int* ptr) const { printf("Deleting int pointer!/n"); delete ptr; } }; struct MyArrayDeleter { void operator()(int* ptr) const { printf("Deleting Array[]!/n"); delete [] ptr; } }; { // example 1 - calls MySimpleDeleter where delete simple pointer is called std::unique_ptr<int, MySimpleDeleter> ptr1(new int(5)); // example 2 - correctly calls MyArrayDeleter where delete[] is called std::unique_ptr<int[], MyArrayDeleter> ptr2(new int[5]); // example 3 - this works (but default_delete<int[]> would have been passed // even if I did not specialize it as it is the default second arg // I only show it here to highlight the problem I am trying to solve std::unique_ptr<int[], std::default_delete<int[]>> ptr2(new int[100]); // example 3 - this lambda is called correctly - I want to do this for arrays std::unique_ptr<int, std::function<void (int *)>> ptr3( new int(3), [&](int *ptr){ delete ptr; std::cout << "delete int* called" << std::endl; }); // example 4 - I cannot get the following like to compile // PLEASE HELP HERE - I cannot get this to compile std::unique_ptr<int[], std::function<void (int *)>> ptr4( new int[4], [&](int *ptr){ delete []ptr; std::cout << "delete [] called" << std::endl; }); } The compiler error is as follows: The error from the compiler (which complains about the new int[4] for ptr4 below is: ''std::unique_ptr<_Ty,_Dx>::unique_ptr'' : cannot access private member declared in class ''std::unique_ptr<_Ty,_Dx>'' 1> with 1> [ 1> _Ty=int [], 1> _Dx=std::tr1::function<void (int *)> 1> ] 1> c:/program files (x86)/microsoft visual studio 10.0/vc/include/memory(2513) : see declaration of ''std::unique_ptr<_Ty,_Dx>::unique_ptr'' 1> with 1> [ 1> _Ty=int [], 1> _Dx=std::tr1::function<void (int *)> 1> ]


Primero que nada, uso VC2010 con SP1, Mingw g ++ 4.7.1

Para array new, unique_ptr ya lo admite de forma limpia:

struct X { X() { puts("ctor"); } ~X() { puts("dtor"); } }; unique_ptr<X[]> xp(new X[3]);

El resultado es:

ctor ctor ctor dtor dtor dtor

Para el eliminador personalizado, desafortunadamente, es inconsistente entre VC2010 y g ++:

VC2010:

unique_ptr<FILE, function<void (FILE*)> > fp(fopen("tmp.txt", "w"), [](FILE *fp){ puts("close file now"); fclose(fp); });

g ++:

unique_ptr<FILE, void (*)(FILE*) > fp(fopen("tmp.txt", "w"), [](FILE *fp){ puts("close file now"); fclose(fp); });

El método de Managu está muy bien, porque en línea lambda es genial, pero duele la legibilidad en mi humilde opinión. También enfatiza ese recurso de liberación antes de la adquisición (RAII).

Aquí sugiero una forma declarativa de separar la adquisición y el lanzamiento de recursos (Scope Guard, funciona tanto para VC2010 como para g ++ 4.7.1):

template<typename T> struct ScopeGuard { T deleter_; ScopeGuard( T deleter) : deleter_(deleter) {} ~ScopeGuard() { deleter_() ; } }; #define UNI_NAME(name, line) name ## line #define ON_OUT_OF_SCOPE_2(lambda_body, line) auto UNI_NAME(deleter_lambda_, line) = [&]() { lambda_body; } ; / ScopeGuard<decltype(UNI_NAME(deleter_lambda_, line))> / UNI_NAME(scope_guard_, line) ( UNI_NAME(deleter_lambda_, line )); #define ON_OUT_OF_SCOPE(lambda_body) ON_OUT_OF_SCOPE_2(lambda_body, __LINE__) FILE * fp = fopen("tmp.txt", "w"); ON_OUT_OF_SCOPE( { puts("close file now"); fclose(fp); } );

El punto es que puede obtener un recurso de la manera antigua y clara, y declarar la declaración para liberar el recurso inmediatamente después de la línea de adquisición de recursos.

El inconveniente es que no puede reenviar un solo objeto junto con su eliminador.

Para FILE *, shared_ptr se puede utilizar como un puntero alternativo para el mismo propósito (tal vez un poco pesado, pero funciona bien tanto para VC2010 como para g ++)

shared_ptr fp2 (fopen ("tmp.txt", "w"), [] (FILE * fp) {fclose (fp); puts ("archivo cerrado");});


Qué pasa:

auto deleter=[&](int* ptr){...}; std::unique_ptr<int[], decltype(deleter)> ptr4(new int[4], deleter);