Why can I not push_back a unique_ptr into a vector?

188,579

Solution 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));

In C++14 we have an even better way to do so:

make_unique<int>(5);

Solution 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.

Share:
188,579

Related videos on Youtube

user383352
Author by

user383352

Professional dabbler.

Updated on December 04, 2020

Comments

  • user383352
    user383352 over 3 years

    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
    
  • UncleBens
    UncleBens almost 14 years
    Since there can be only one, one should also be able to pass a temporary directly to the vector: vec.push_back(std::unique_ptr<int>(new int(1)));. unique_ptr can also use a custom deleter (which does nothing), but then one must take into account that the address of the local variable becomes invalid at the end of the scope.
  • deft_code
    deft_code almost 14 years
    Another option is to use emplace_back. e.g. vec.emplace_back(new int(1));
  • James McNellis
    James McNellis about 12 years
    @deft_code: No, that is not safe. The emplace_back operation can throw, and if it does, the dynamically allocated int will be leaked. The rule of thumb is that all dynamic allocations should be owned by a named smart pointer to avoid leakiness.
  • David Stone
    David Stone about 11 years
    Or returned from a function: see make_shared.
  • cdmh
    cdmh about 11 years
    make_shared() returns a shared_ptr, not a unique_ptr. Unfortunately, there is no make_unique() in C++11; an unfortunate omission that hopefully will be fixed in C++14
  • FKaria
    FKaria over 10 years
    @cdmh Why would you need a make_unique() ? I can see that make_shared() allocates one bigger block of memory at the time instead of two blocks as when you use new, so is faster. but I don´t see the advantadge od having it for unique_ptr.
  • cdmh
    cdmh over 10 years
    @FKaria make_unique() would mean that new never needs to be invoked directly, which changes the programmer's mindset and avoids (significantly reduces) memory leaks. Advice like "Avoid new and delete" can then appear in the next edition of Meyers/Alexandrescu/Sutter's book :)
  • Qix - MONICA WAS MISTREATED
    Qix - MONICA WAS MISTREATED over 6 years
    See this comment - using emplace_x() functions are unsafe when using smart pointers.
  • Franklin Yu
    Franklin Yu about 4 years
    std::make_unique() made it to C++14. Please mentioned it in the answer.
  • user2189731
    user2189731 about 4 years
    So what's the best way to store an unique_ptr into vector? It's extremely slow compared with raw pointer as I tested.
  • Groo
    Groo about 3 years
    @user2189731: that's pretty strange, both std::vector and std::unique_ptr are templated classes which should allow heavy compiler optimization. The only thing std::unique_ptr does is prevent copy assignment, everything else should be just optimized away to raw pointer access.
  • Pavel Šimerda
    Pavel Šimerda about 3 years
    @cdmh On the other hand, make_unique() can be used with an Initializer List while new can.
  • yushang
    yushang almost 3 years
    @JamesMcNellis but std::vector<std::unique_ptr<int>> v { std::move(std::make_unique<int>(1)), }; does not work