Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Error with `std::vector< std::unique_ptr< T > >`

I'm seeing some errors passing std::vector< std::unique_ptr< T > > around with std::move. The code that reproduces the problem is this:

#include <memory>    // for std::unique_ptr
#include <utility>   // for std::move
#include <vector>    // for std::vector

struct bar {};
using vtype = std::vector<std::unique_ptr<bar>>;

struct foo
{
    foo(vtype v) : _v(std::move(v)) { }
private:
    vtype _v;
};

vtype getVector()
{
    return { std::move( std::unique_ptr<bar>(new bar()) ) };
};    

int main()
{
foo f(std::move(getVector()));
};

With clang 3.4, this code produces this error:

$ clang++ -std=c++11 test.cpp -o xtest
In file included from test.cpp:1:
In file included from /usr/bin/../lib/gcc/x86_64-linux-gnu/4.8/../../../../include/c++/4.8/memory:64:
/usr/bin/../lib/gcc/x86_64-linux-gnu/4.8/../../../../include/c++/4.8/bits/stl_construct.h:75:38: error: call to deleted constructor of
      'std::unique_ptr<bar, std::default_delete<bar> >'
    { ::new(static_cast<void*>(__p)) _T1(std::forward<_Args>(__args)...); }
                                     ^   ~~~~~~~~~~~~~~~~~~~~~~~~~~~
/usr/bin/../lib/gcc/x86_64-linux-gnu/4.8/../../../../include/c++/4.8/bits/stl_uninitialized.h:75:8: note: in instantiation of function template specialization
      'std::_Construct<std::unique_ptr<bar, std::default_delete<bar> >, const std::unique_ptr<bar, std::default_delete<bar> > &>' requested here
                std::_Construct(std::__addressof(*__cur), *__first);
                     ^
/usr/bin/../lib/gcc/x86_64-linux-gnu/4.8/../../../../include/c++/4.8/bits/stl_uninitialized.h:117:2: note: in instantiation of function template specialization
      'std::__uninitialized_copy<false>::__uninit_copy<const std::unique_ptr<bar, std::default_delete<bar> > *, std::unique_ptr<bar, std::default_delete<bar> > *>'
      requested here
        __uninit_copy(__first, __last, __result);
        ^
/usr/bin/../lib/gcc/x86_64-linux-gnu/4.8/../../../../include/c++/4.8/bits/stl_uninitialized.h:258:19: note: in instantiation of function template specialization
      'std::uninitialized_copy<const std::unique_ptr<bar, std::default_delete<bar> > *, std::unique_ptr<bar, std::default_delete<bar> > *>' requested here
    { return std::uninitialized_copy(__first, __last, __result); }
                  ^
/usr/bin/../lib/gcc/x86_64-linux-gnu/4.8/../../../../include/c++/4.8/bits/stl_vector.h:1204:11: note: in instantiation of function template specialization
      'std::__uninitialized_copy_a<const std::unique_ptr<bar, std::default_delete<bar> > *, std::unique_ptr<bar, std::default_delete<bar> > *, std::unique_ptr<bar,
      std::default_delete<bar> > >' requested here
            std::__uninitialized_copy_a(__first, __last,
                 ^
/usr/bin/../lib/gcc/x86_64-linux-gnu/4.8/../../../../include/c++/4.8/bits/stl_vector.h:368:2: note: in instantiation of function template specialization
      'std::vector<std::unique_ptr<bar, std::default_delete<bar> >, std::allocator<std::unique_ptr<bar, std::default_delete<bar> > > >::_M_range_initialize<const
      std::unique_ptr<bar, std::default_delete<bar> > *>' requested here
        _M_range_initialize(__l.begin(), __l.end(),
        ^
test.cpp:17:12: note: in instantiation of member function 'std::vector<std::unique_ptr<bar, std::default_delete<bar> >, std::allocator<std::unique_ptr<bar,
      std::default_delete<bar> > > >::vector' requested here
    return { std::move( std::unique_ptr<bar>(new bar()) ) };
           ^
/usr/bin/../lib/gcc/x86_64-linux-gnu/4.8/../../../../include/c++/4.8/bits/unique_ptr.h:273:7: note: function has been explicitly marked deleted here
      unique_ptr(const unique_ptr&) = delete;
      ^
1 error generated.

The situations doesn't seem to be any better with g++ 4.8:

 $ g++-4.8 -std=c++11 test.cpp -o xtest
In file included from /usr/include/c++/4.8/memory:64:0,
                 from test.cpp:1:
/usr/include/c++/4.8/bits/stl_construct.h: In instantiation of ‘void std::_Construct(_T1*, _Args&& ...) [with _T1 = std::unique_ptr<bar>; _Args = {const std::unique_ptr<bar, std::default_delete<bar> >&}]’:
/usr/include/c++/4.8/bits/stl_uninitialized.h:75:53:   required from ‘static _ForwardIterator std::__uninitialized_copy<_TrivialValueTypes>::__uninit_copy(_InputIterator, _InputIterator, _ForwardIterator) [with _InputIterator = const std::unique_ptr<bar>*; _ForwardIterator = std::unique_ptr<bar>*; bool _TrivialValueTypes = false]’
/usr/include/c++/4.8/bits/stl_uninitialized.h:117:41:   required from ‘_ForwardIterator std::uninitialized_copy(_InputIterator, _InputIterator, _ForwardIterator) [with _InputIterator = const std::unique_ptr<bar>*; _ForwardIterator = std::unique_ptr<bar>*]’
/usr/include/c++/4.8/bits/stl_uninitialized.h:258:63:   required from ‘_ForwardIterator std::__uninitialized_copy_a(_InputIterator, _InputIterator, _ForwardIterator, std::allocator<_Tp>&) [with _InputIterator = const std::unique_ptr<bar>*; _ForwardIterator = std::unique_ptr<bar>*; _Tp = std::unique_ptr<bar>]’
/usr/include/c++/4.8/bits/stl_vector.h:1206:27:   required from ‘void std::vector<_Tp, _Alloc>::_M_range_initialize(_ForwardIterator, _ForwardIterator, std::forward_iterator_tag) [with _ForwardIterator = const std::unique_ptr<bar>*; _Tp = std::unique_ptr<bar>; _Alloc = std::allocator<std::unique_ptr<bar> >]’
/usr/include/c++/4.8/bits/stl_vector.h:369:36:   required from ‘std::vector<_Tp, _Alloc>::vector(std::initializer_list<_Tp>, const allocator_type&) [with _Tp = std::unique_ptr<bar>; _Alloc = std::allocator<std::unique_ptr<bar> >; std::vector<_Tp, _Alloc>::allocator_type = std::allocator<std::unique_ptr<bar> >]’
test.cpp:17:59:   required from here
/usr/include/c++/4.8/bits/stl_construct.h:75:7: error: use of deleted function ‘std::unique_ptr<_Tp, _Dp>::unique_ptr(const std::unique_ptr<_Tp, _Dp>&) [with _Tp = bar; _Dp = std::default_delete<bar>]’
     { ::new(static_cast<void*>(__p)) _T1(std::forward<_Args>(__args)...); }
       ^
In file included from /usr/include/c++/4.8/memory:81:0,
                 from test.cpp:1:
/usr/include/c++/4.8/bits/unique_ptr.h:273:7: error: declared here
       unique_ptr(const unique_ptr&) = delete;
       ^

According to this answer and the comments, this shouldn't be happening on these compilers, but I'm not doing exactly the same: I'm trying to initialize the vector with an initializer list.

Any idea what needs to happen in order for this code to build correctly?

like image 991
lurscher Avatar asked Jul 22 '15 01:07

lurscher


People also ask

What is std :: unique_ptr?

std::unique_ptr is a smart pointer that owns and manages another object through a pointer and disposes of that object when the unique_ptr goes out of scope. The object is disposed of, using the associated deleter when either of the following happens: the managing unique_ptr object is destroyed.

What happens when unique_ptr goes out of scope?

An​ unique_ptr has exclusive ownership of the object it points to and ​will destroy the object when the pointer goes out of scope. A unique_ptr explicitly prevents copying of its contained pointer. Instead, the std::move function has to be used to transfer ownership of the contained pointer to another unique_ptr .

How to transfer ownership unique_ ptr?

In C++11 we can transfer the ownership of an object to another unique_ptr using std::move() . After the ownership transfer, the smart pointer that ceded the ownership becomes null and get() returns nullptr.

What is the use of std :: Make_unique?

In this case, std::make_unique provides a "Basic Exception Safety" that the memory allocated and object created by new will never be orphaned no matter what.


1 Answers

The used of the braced-init-list in the return statement within getVector

return { std::move( std::unique_ptr<bar>(new bar()) ) };

results in a call to the std::vector<T> constructor that takes an initializer_list<T> argument. Even though you're moving the unique_ptr, an initializer_list only allows const access to its elements, due to which the vector will attempt to copy the unique_ptr, leading to the error you see.

You can fix the error by resorting to a more verbose manner of constructing the vector

vtype getVector()
{
    vtype v;
    v.push_back(std::unique_ptr<bar>(new bar()));
    return v;
}  

Live demo


For curiosity's sake, it is possible to construct a vector from an array of move-only objects, but you need to go through std::move_iterator to move the elements.

vtype getVector()
{
    std::unique_ptr<bar> arr[] = {std::unique_ptr<bar>(new bar())};
    return {std::make_move_iterator(std::begin(arr)),
            std::make_move_iterator(std::end(arr))};
}
like image 105
Praetorian Avatar answered Sep 27 '22 22:09

Praetorian