Por que não posso enviar push_back um unique_ptr para um vetor?

217

O que há de errado com 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;
}

O erro:

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
fonte

Respostas:

328

Você precisa mover o unique_ptr:

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

unique_ptrgarante que um único unique_ptrcontêiner possua propriedade do ponteiro retido. Isso significa que você não pode fazer cópias de a unique_ptr(porque dois unique_ptrs teriam propriedade); portanto, você só pode movê-lo.

Observe, no entanto, que seu uso atual unique_ptrestá incorreto. Você não pode usá-lo para gerenciar um ponteiro para uma variável local. O tempo de vida de uma variável local é gerenciado automaticamente: as variáveis ​​locais são destruídas quando o bloco termina (por exemplo, quando a função retorna, neste caso). Você precisa alocar dinamicamente o objeto:

std::unique_ptr<int> ptr(new int(1));
James McNellis
fonte
12
Como pode haver apenas um, a pessoa também deve ser capaz de passar um temporária diretamente ao vetor: vec.push_back(std::unique_ptr<int>(new int(1)));. unique_ptrtambém pode usar um deleter personalizado (que não faz nada), mas é necessário levar em consideração que o endereço da variável local se torna inválido no final do escopo.
UncleBens 19/07
18
Outra opção é usar emplace_back. por exemplovec.emplace_back(new int(1));
deft_code 19/07/10
75
@deft_code: Não, isso não é seguro. A emplace_backoperação pode lançar e, se o fizer, o alocado dinamicamente intserá vazado. A regra geral é que todas as alocações dinâmicas devem pertencer a um ponteiro inteligente nomeado para evitar vazamentos.
James McNellis
8
make_shared () retorna um shared_ptr, não um unique_ptr. Infelizmente, não há make_unique () no C ++ 11; uma omissão lamentável que esperamos que vai ser fixo no C ++ 14
cdmh
29
@FKaria make_unique () significaria que newnunca precisa ser chamado diretamente, o que altera a mentalidade do programador e evita (reduz significativamente) vazamentos de memória. Conselhos como "Evite novo e excluir" pode aparecer na próxima edição da Meyers / livro de Alexandrescu / Sutter :)
cdmh
24

std :: unique_ptr não tem construtor de cópias. Você cria uma instância e pede ao std :: vector que copie essa instância durante a inicialização.

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>]'

A classe atende aos requisitos de MoveConstructible e MoveAssignable, mas não os requisitos de CopyConstructible ou CopyAssignable.

O seguinte funciona com as novas chamadas emplace .

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

Consulte o uso de unique_ptr com contêineres de biblioteca padrão para leitura adicional.

Ben Crowhurst
fonte
5
Veja este comentário - o uso de emplace_x()funções não é seguro ao usar ponteiros inteligentes.
Qix - MONICA FOI ERRADA em
Então, qual é a melhor maneira de armazenar um unique_ptr em vetor? É extremamente lento em comparação com o ponteiro bruto, como eu testei.
user2189731 9/04