Pourquoi ne puis-je pas repousser un unique_ptr dans un vecteur?

217

Quel est le problème avec ce programme?

#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'erreur:

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
user383352
la source

Réponses:

328

Vous devez déplacer le unique_ptr:

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

unique_ptrgarantit qu'un seul unique_ptrconteneur est propriétaire du pointeur détenu. Cela signifie que vous ne pouvez pas faire de copie d'un unique_ptr(car alors deux unique_ptrs auraient la propriété), vous ne pouvez donc que le déplacer.

Notez cependant que votre utilisation actuelle de unique_ptrest incorrecte. Vous ne pouvez pas l'utiliser pour gérer un pointeur vers une variable locale. La durée de vie d'une variable locale est gérée automatiquement: les variables locales sont détruites à la fin du bloc (par exemple, lorsque la fonction revient, dans ce cas). Vous devez allouer dynamiquement l'objet:

std::unique_ptr<int> ptr(new int(1));
James McNellis
la source
12
Comme il peut y avoir qu'un seul, il faut aussi être en mesure de passer un directement temporaire au vecteur: vec.push_back(std::unique_ptr<int>(new int(1)));. unique_ptrpeut également utiliser un suppresseur personnalisé (qui ne fait rien), mais il faut alors tenir compte du fait que l'adresse de la variable locale devient invalide à la fin de la portée.
UncleBens
18
Une autre option consiste à utiliser emplace_back. par exemplevec.emplace_back(new int(1));
deft_code
75
@deft_code: Non, ce n'est pas sûr. L' emplace_backopération peut lancer, et si c'est le cas, l'allocation dynamique intsera divulguée. La règle générale est que toutes les allocations dynamiques doivent appartenir à un pointeur intelligent nommé pour éviter les fuites.
James McNellis
8
make_shared () renvoie un shared_ptr, pas un unique_ptr. Malheureusement, il n'y a pas de make_unique () en C ++ 11; une omission malheureuse qui, espérons-le, sera corrigée en C ++ 14
cdmh
29
@FKaria make_unique () signifierait qu'il newn'a jamais besoin d'être appelé directement, ce qui change l'état d'esprit du programmeur et évite (réduit considérablement) les fuites de mémoire. Des conseils comme «Evitez les nouveaux et supprimez» peuvent alors apparaître dans la prochaine édition du livre de Meyers / Alexandrescu / Sutter :)
cdmh
24

std :: unique_ptr n'a pas de constructeur de copie. Vous créez une instance puis demandez au std :: vector de copier cette instance lors de l'initialisation.

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 satisfait les exigences de MoveConstructible et MoveAssignable, mais pas les exigences de CopyConstructible ou CopyAssignable.

Ce qui suit fonctionne avec les nouveaux emplace appels.

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

Voir utilisation de unique_ptr avec des conteneurs de bibliothèque standard pour de plus amples informations.

Ben Crowhurst
la source
5
Voir ce commentaire - l'utilisation de emplace_x()fonctions n'est pas sûre lors de l'utilisation de pointeurs intelligents.
Qix - MONICA A ÉTÉ BRUÉE
Alors, quelle est la meilleure façon de stocker un unique_ptr dans un vecteur? C'est extrêmement lent par rapport au pointeur brut que j'ai testé.
user2189731