¿Por qué no puedo retroceder un_ptr_unico en un vector?

217

¿Qué tiene de malo este programa?

#include <memory>
#include <vector>

int main()
{
    std::vector<std::unique_ptr<int>> vec;

    int x(1);
    std::unique_ptr<int> ptr2x(&x);
    vec.push_back(ptr2x); //This tiny command has a vicious error.

    return 0;
}

El error:

In file included from c:\mingw\bin\../lib/gcc/mingw32/4.5.0/include/c++/mingw32/bits/c++allocator.h:34:0,
                 from c:\mingw\bin\../lib/gcc/mingw32/4.5.0/include/c++/bits/allocator.h:48,
                 from c:\mingw\bin\../lib/gcc/mingw32/4.5.0/include/c++/memory:64,
                 from main.cpp:6:
c:\mingw\bin\../lib/gcc/mingw32/4.5.0/include/c++/bits/unique_ptr.h: In member function 'void __gnu_cxx::new_allocator<_Tp>::construct(_Tp*, const _Tp&) [with _Tp = std::unique_ptr<int>, _Tp* = std::unique_ptr<int>*]':
c:\mingw\bin\../lib/gcc/mingw32/4.5.0/include/c++/bits/stl_vector.h:745:6:   instantiated from 'void std::vector<_Tp, _Alloc>::push_back(const value_type&) [with _Tp = std::unique_ptr<int>, _Alloc = std::allocator<std::unique_ptr<int> >, value_type = std::unique_ptr<int>]'
main.cpp:16:21:   instantiated from here
c:\mingw\bin\../lib/gcc/mingw32/4.5.0/include/c++/bits/unique_ptr.h:207:7: error: deleted function 'std::unique_ptr<_Tp, _Tp_Deleter>::unique_ptr(const std::unique_ptr<_Tp, _Tp_Deleter>&) [with _Tp = int, _Tp_Deleter = std::default_delete<int>, std::unique_ptr<_Tp, _Tp_Deleter> = std::unique_ptr<int>]'
c:\mingw\bin\../lib/gcc/mingw32/4.5.0/include/c++/ext/new_allocator.h:105:9: error: used here
In file included from c:\mingw\bin\../lib/gcc/mingw32/4.5.0/include/c++/vector:69:0,
                 from main.cpp:7:
c:\mingw\bin\../lib/gcc/mingw32/4.5.0/include/c++/bits/unique_ptr.h: In member function 'void std::vector<_Tp, _Alloc>::_M_insert_aux(std::vector<_Tp, _Alloc>::iterator, _Args&& ...) [with _Args = {const std::unique_ptr<int>&}, _Tp = std::unique_ptr<int>, _Alloc = std::allocator<std::unique_ptr<int> >, std::vector<_Tp, _Alloc>::iterator = __gnu_cxx::__normal_iterator<std::unique_ptr<int>*, std::vector<std::unique_ptr<int> > >, typename std::vector<_Tp, _Alloc>::_Base::_Tp_alloc_type::pointer = std::unique_ptr<int>*]':
c:\mingw\bin\../lib/gcc/mingw32/4.5.0/include/c++/bits/stl_vector.h:749:4:   instantiated from 'void std::vector<_Tp, _Alloc>::push_back(const value_type&) [with _Tp = std::unique_ptr<int>, _Alloc = std::allocator<std::unique_ptr<int> >, value_type = std::unique_ptr<int>]'
main.cpp:16:21:   instantiated from here
c:\mingw\bin\../lib/gcc/mingw32/4.5.0/include/c++/bits/unique_ptr.h:207:7: error: deleted function 'std::unique_ptr<_Tp, _Tp_Deleter>::unique_ptr(const std::unique_ptr<_Tp, _Tp_Deleter>&) [with _Tp = int, _Tp_Deleter = std::default_delete<int>, std::unique_ptr<_Tp, _Tp_Deleter> = std::unique_ptr<int>]'
c:\mingw\bin\../lib/gcc/mingw32/4.5.0/include/c++/bits/vector.tcc:314:4: error: used here
usuario383352
fuente

Respuestas:

328

Necesitas mover el unique_ptr:

vec.push_back(std::move(ptr2x));

unique_ptrgarantiza que un único unique_ptrcontenedor tenga la propiedad del puntero retenido. Esto significa que no puede hacer copias de un unique_ptr(porque entonces dos unique_ptrs tendrían la propiedad), por lo que solo puede moverlo.

Sin embargo, tenga en cuenta que su uso actual de unique_ptres incorrecto. No puede usarlo para administrar un puntero a una variable local. La vida útil de una variable local se gestiona automáticamente: las variables locales se destruyen cuando finaliza el bloque (por ejemplo, cuando la función regresa, en este caso). Necesita asignar dinámicamente el objeto:

std::unique_ptr<int> ptr(new int(1));
James McNellis
fuente
12
Dado que sólo puede haber uno, uno también debe ser capaz de pasar una temporal directamente al vector: vec.push_back(std::unique_ptr<int>(new int(1)));. unique_ptrTambién puede usar un eliminador personalizado (que no hace nada), pero luego se debe tener en cuenta que la dirección de la variable local deja de ser válida al final del alcance.
UncleBens
18
Otra opción es usar emplace_back. por ejemplovec.emplace_back(new int(1));
deft_code el
75
@deft_code: No, eso no es seguro. La emplace_backoperación puede lanzar, y si lo hace, la asignación dinámica intse filtrará. La regla general es que todas las asignaciones dinámicas deben ser propiedad de un puntero inteligente con nombre para evitar fugas.
James McNellis
8
make_shared () devuelve un shared_ptr, no un unique_ptr. Desafortunadamente, no hay make_unique () en C ++ 11; una desafortunada omisión que con suerte se solucionará en C ++ 14
cdmh
29
@FKaria make_unique () significaría que newnunca necesita ser invocado directamente, lo que cambia la mentalidad del programador y evita (reduce significativamente) las pérdidas de memoria. Consejos como "Evitar nuevo y eliminar" pueden aparecer en la próxima edición del libro de Meyers / Alexandrescu / Sutter :)
cdmh
24

std :: unique_ptr no tiene constructor de copia. Usted crea una instancia y luego le pide al vector std :: que copie esa instancia durante la inicialización.

error: deleted function 'std::unique_ptr<_Tp, _Tp_Deleter>::uniqu
e_ptr(const std::unique_ptr<_Tp, _Tp_Deleter>&) [with _Tp = int, _Tp_D
eleter = std::default_delete<int>, std::unique_ptr<_Tp, _Tp_Deleter> =
 std::unique_ptr<int>]'

La clase satisface los requisitos de MoveConstructible y MoveAssignable, pero no los requisitos de CopyConstructible o CopyAssignable.

Lo siguiente funciona con las nuevas llamadas de emplazamiento .

std::vector< std::unique_ptr< int > > vec;
vec.emplace_back( new int( 1984 ) );

Consulte el uso de unique_ptr con contenedores de biblioteca estándar para obtener más información.

Ben Crowhurst
fuente
55
Vea este comentario : el uso de emplace_x()funciones no es seguro cuando se utilizan punteros inteligentes.
Qix - MONICA FUE MAL
Entonces, ¿cuál es la mejor manera de almacenar un unique_ptr en un vector? Es extremadamente lento en comparación con el puntero en bruto como lo probé.
user2189731