Why can I not push_back a unique_ptr into a vector

2018-12-31 19:02发布

问题:

What is wrong with this program?

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

The 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

回答1:

You need to move the unique_ptr:

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

unique_ptr guarantees that a single unique_ptr container has ownership of the held pointer. This means that you can\'t make copies of a unique_ptr (because then two unique_ptrs would have ownership), so you can only move it.

Note, however, that your current use of unique_ptr is incorrect. You cannot use it to manage a pointer to a local variable. The lifetime of a local variable is managed automatically: local variables are destroyed when the block ends (e.g., when the function returns, in this case). You need to dynamically allocate the object:

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


回答2:

std::unique_ptr has no copy constructor. You create an instance and then ask the std::vector to copy that instance during 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>]\'

The class satisfies the requirements of MoveConstructible and MoveAssignable, but not the requirements of either CopyConstructible or CopyAssignable.

The following works with the new emplace calls.

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

See using unique_ptr with standard library containers for further reading.