Perché non posso push_back un unique_ptr in un vettore?


217

Cosa c'è di sbagliato in questo programma?

#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;
}

L'errore:

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

Risposte:


328

Devi spostare il unique_ptr:

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

unique_ptrgarantisce che un singolo unique_ptrcontenitore abbia la proprietà del puntatore trattenuto. Ciò significa che non puoi fare copie di un unique_ptr(perché allora due unique_ptrs avrebbero la proprietà), quindi puoi solo spostarlo.

Si noti, tuttavia, che l'attuale utilizzo di unique_ptrnon è corretto. Non puoi usarlo per gestire un puntatore a una variabile locale. La durata di una variabile locale viene gestita automaticamente: le variabili locali vengono distrutte al termine del blocco (ad esempio, quando la funzione ritorna, in questo caso). Devi allocare dinamicamente l'oggetto:

std::unique_ptr<int> ptr(new int(1));

12
Poiché non vi può essere solo una, si dovrebbe anche essere in grado di passare una temporanea direttamente al vettore: vec.push_back(std::unique_ptr<int>(new int(1)));. unique_ptrpuò anche utilizzare un deleter personalizzato (che non fa nulla), ma è necessario tenere conto del fatto che l'indirizzo della variabile locale diventa non valido alla fine dell'ambito.
UncleBens,

18
Un'altra opzione è usare emplace_back. es.vec.emplace_back(new int(1));
deft_code

75
@deft_code: No, non è sicuro. L' emplace_backoperazione può essere lanciata e, in caso affermativo, le perdite allocate dinamicamente int. La regola empirica è che tutte le allocazioni dinamiche dovrebbero essere di proprietà di un puntatore intelligente denominato per evitare perdite.
James McNellis,

8
make_shared () restituisce un shared_ptr, non un unique_ptr. Sfortunatamente, non esiste make_unique () in C ++ 11; una sfortunata omissione che si spera venga risolta in C ++ 14
cdmh

29
@FKaria make_unique () significherebbe che newnon deve mai essere invocato direttamente, il che cambia la mentalità del programmatore ed evita (riduce significativamente) le perdite di memoria. Consigli come "Evita nuovo ed elimina" possono quindi apparire nella prossima edizione del libro di Meyers / Alexandrescu / Sutter :)
cdmh

23

std :: unique_ptr non ha costruttore di copie. Si crea un'istanza e quindi si chiede allo std :: vector di copiarla durante l'inizializzazione.

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 classe soddisfa i requisiti di MoveConstructible e MoveAssignable, ma non i requisiti di CopyConstructible o CopyAssignable.

Quanto segue funziona con le nuove chiamate emplace .

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

Per ulteriori informazioni, vedi l' utilizzo di unique_ptr con i contenitori di libreria standard .


5
Vedi questo commento : l'utilizzo delle emplace_x()funzioni non è sicuro quando si utilizzano i puntatori intelligenti.
Qix - MONICA È STATA MISTREATA il

Quindi qual è il modo migliore per memorizzare un unique_ptr in vettoriale? È estremamente lento rispetto al puntatore non elaborato durante il test.
user2189731
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.