Почему я не могу push_back уникальный_птр в вектор?

217

Что не так с этой программой?

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

Ошибка:

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
источник

Ответы:

328

Вам необходимо переместить unique_ptr:

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

unique_ptrгарантирует, что один unique_ptrконтейнер владеет удерживаемым указателем. Это означает, что вы не можете делать копии a unique_ptr(потому что тогда два unique_ptrs будут владеть), поэтому вы можете только перемещать его.

Однако обратите внимание, что ваше текущее использование unique_ptrневерно. Вы не можете использовать его для управления указателем на локальную переменную. Время жизни локальной переменной управляется автоматически: локальные переменные уничтожаются, когда заканчивается блок (например, когда функция возвращается, в этом случае). Вам необходимо динамически выделить объект:

std::unique_ptr<int> ptr(new int(1));
Джеймс МакНеллис
источник
12
Поскольку может быть только один, один должен также быть в состоянии передать временное непосредственно к вектору: vec.push_back(std::unique_ptr<int>(new int(1)));. unique_ptrМожно также использовать пользовательское средство удаления (которое ничего не делает), но затем необходимо учитывать, что адрес локальной переменной становится недействительным в конце области.
UncleBens
18
Другим вариантом является использование emplace_back. например,vec.emplace_back(new int(1));
deft_code
76
@deft_code: Нет, это не безопасно. emplace_backОперация может бросить, и если это произойдет, динамически распределяемой intбудет утечка. Практическое правило заключается в том, что все динамические выделения должны принадлежать именованному интеллектуальному указателю, чтобы избежать утечки.
Джеймс МакНеллис
8
make_shared () возвращает shared_ptr, а не unique_ptr. К сожалению, в C ++ 11 нет make_unique (); досадное упущение, которое, будем надеяться, будет исправлено в C ++ 14
cdmh
29
@FKaria make_unique () будет означать, что newникогда не нужно вызывать напрямую, что меняет мышление программиста и предотвращает (значительно уменьшает) утечки памяти. Совет типа «Избегайте новых и удаляй» может появиться в следующем издании книги Мейерса / Александреску / Саттера :)
cdmh
23

У std :: unique_ptr нет конструктора копирования. Вы создаете экземпляр и затем просите std :: vector скопировать этот экземпляр во время инициализации.

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

Класс удовлетворяет требованиям MoveConstructible и MoveAssignable, но не требованиям CopyConstructible или CopyAssignable.

Следующее работает с новыми вызовами emplace .

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

См. Использование unique_ptr со стандартными контейнерами библиотеки для дальнейшего чтения.

Бен Кроухерст
источник
5
Смотрите этот комментарий - использование emplace_x()функций небезопасно при использовании умных указателей.
Qix - МОНИКА ПРОИЗОШЛА
Итак, как лучше всего сохранить unique_ptr в векторе? Это очень медленно по сравнению с необработанным указателем, как я тестировал.
user2189731