241

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
1201ProgramAlarm
  • 30,320
  • 7
  • 40
  • 49
user383352
  • 4,443
  • 4
  • 22
  • 20

2 Answers2

358

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);
D.R.
  • 17,368
  • 19
  • 70
  • 158
James McNellis
  • 327,682
  • 71
  • 882
  • 954
  • 12
    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(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. – UncleBens Jul 19 '10 at 18:39
  • 19
    Another option is to use `emplace_back`. e.g. `vec.emplace_back(new int(1));` – deft_code Jul 19 '10 at 19:12
  • 82
    @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. – James McNellis Feb 20 '12 at 05:52
  • Or returned from a function: see `make_shared`. – David Stone Mar 28 '13 at 18:17
  • 8
    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 – cdmh Apr 14 '13 at 19:59
  • 1
    @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`. – FKaria Oct 19 '13 at 04:23
  • 29
    @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 :) – cdmh Oct 19 '13 at 07:37
  • 1
    [`std::make_unique()`](https://en.cppreference.com/w/cpp/memory/unique_ptr/make_unique) made it to C++14. Please mentioned it in the answer. – Franklin Yu Mar 03 '20 at 01:43
  • @cdmh On the other hand, `make_unique()` can be used with an Initializer List while `new` can. – Pavel Šimerda Mar 03 '21 at 11:27
25

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.

Ben Crowhurst
  • 7,042
  • 5
  • 40
  • 72
  • 7
    See [this comment](https://stackoverflow.com/questions/3283778/why-can-i-not-push-back-a-unique-ptr-into-a-vector#comment11813799_3283795) - using `emplace_x()` functions are unsafe when using smart pointers. – Qix - MONICA WAS MISTREATED Dec 22 '17 at 07:43
  • 2
    So what's the best way to store an unique_ptr into vector? It's extremely slow compared with raw pointer as I tested. – user2189731 Apr 09 '20 at 07:35
  • @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. – Groo Feb 07 '21 at 22:39