2

Coming back to C++ after many years; trying to catch up to C++11 & 14. I've read about rvalues and move semantics. I thought I understood the concept. Apparently not. I've looked at dozens of examples. But I simply can't get my code to compile. I must be missing something obvious in the examples. I always get the error about the copy ctor being deleted because of unique_ptr<int> having a user-declared move ctor. There's obviously something I'm missing about the concept, but I can't figure out what it is. Here's the code, stripped down to its essence:

#include <memory>
#include <utility>
#include <vector>

int main(int, char*[]) {
  auto oneInt{std::make_unique<int>(0)};
  auto someInts{std::vector<std::unique_ptr<int>>{std::move(oneInt)}};

  return 0;
}

What am I doing wrong?

Edit: Here's the error from this particular code. Note that I've tried every variation on the code that I can think of, with varying results, but the basic problem is always the same: copy ctor deleted because unique_ptr<int> has a user-declared move ctor.

Edit: I've updated the code to #include <memory>, and pasted the new error. I can only wish the problem were something silly like that.

    /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/../include/c++/v1/memory:1752:31: error: call to implicitly-deleted copy constructor of
      'std::__1::unique_ptr<int, std::__1::default_delete<int> >'
            ::new((void*)__p) _Up(_VSTD::forward<_Args>(__args)...);
                              ^   ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/../include/c++/v1/memory:1668:18: note: in instantiation of function template
      specialization 'std::__1::allocator<std::__1::unique_ptr<int, std::__1::default_delete<int> > >::construct<std::__1::unique_ptr<int, std::__1::default_delete<int> >,
      const std::__1::unique_ptr<int, std::__1::default_delete<int> > &>' requested here
            {__a.construct(__p, _VSTD::forward<_Args>(__args)...);}
                 ^
/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/../include/c++/v1/memory:1514:14: note: in instantiation of function template
      specialization 'std::__1::allocator_traits<std::__1::allocator<std::__1::unique_ptr<int, std::__1::default_delete<int> > > >::__construct<std::__1::unique_ptr<int,
      std::__1::default_delete<int> >, const std::__1::unique_ptr<int, std::__1::default_delete<int> > &>' requested here
            {__construct(__has_construct<allocator_type, _Tp*, _Args...>(),
             ^
/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/../include/c++/v1/memory:1598:17: note: in instantiation of function template
      specialization 'std::__1::allocator_traits<std::__1::allocator<std::__1::unique_ptr<int, std::__1::default_delete<int> > > >::construct<std::__1::unique_ptr<int,
      std::__1::default_delete<int> >, const std::__1::unique_ptr<int, std::__1::default_delete<int> > &>' requested here
                construct(__a, _VSTD::__to_raw_pointer(__begin2), *__begin1);
                ^
/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/../include/c++/v1/vector:1024:21: note: in instantiation of function template
      specialization 'std::__1::allocator_traits<std::__1::allocator<std::__1::unique_ptr<int, std::__1::default_delete<int> > > >::__construct_range_forward<const
      std::__1::unique_ptr<int, std::__1::default_delete<int> > *, std::__1::unique_ptr<int, std::__1::default_delete<int> > *>' requested here
    __alloc_traits::__construct_range_forward(__a, __first, __last, this->__end_);
                    ^
/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/../include/c++/v1/vector:1285:9: note: in instantiation of function template
      specialization 'std::__1::vector<std::__1::unique_ptr<int, std::__1::default_delete<int> >, std::__1::allocator<std::__1::unique_ptr<int, std::__1::default_delete<int> >
      > >::__construct_at_end<const std::__1::unique_ptr<int, std::__1::default_delete<int> > *>' requested here
        __construct_at_end(__il.begin(), __il.end(), __il.size());
        ^
virtual.cpp:7:21: note: in instantiation of member function 'std::__1::vector<std::__1::unique_ptr<int, std::__1::default_delete<int> >,
      std::__1::allocator<std::__1::unique_ptr<int, std::__1::default_delete<int> > > >::vector' requested here
      auto someInts{std::vector<std::unique_ptr<int>>{std::move(oneInt)}};
                    ^
/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/../include/c++/v1/memory:2621:31: note: copy constructor is implicitly deleted because
      'unique_ptr<int, std::__1::default_delete<int> >' has a user-declared move constructor
    _LIBCPP_INLINE_VISIBILITY unique_ptr(unique_ptr&& __u) _NOEXCEPT
SaganRitual
  • 3,143
  • 2
  • 24
  • 40
  • 4
    When asking about build errors it usually helps seeing the errors. Can you please edit your question to include the full, complete and unmodified output from the build, as text, and including any possible informational notes? – Some programmer dude Aug 27 '17 at 14:03
  • 4
    For one, you did not include the `` header. – StoryTeller - Unslander Monica Aug 27 '17 at 14:04
  • 2
    `oneInt` is moved into the initialiser list, but it's copied out of it. – molbdnilo Aug 27 '17 at 14:05
  • The biggest problem with your code is it's riddled with unnecessary `auto` and curlies and overwrought declarations. Start with basics, like `std::vector someInts(std::move(oneInt));`. – John Zwinck Aug 27 '17 at 14:08
  • @JohnZwinck: That won't work, since `vector` doesn't have a constructor that takes a single `T`. – Nicol Bolas Aug 27 '17 at 14:15
  • @molbdnilo I don't understand, sorry. – SaganRitual Aug 27 '17 at 14:15
  • @JohnZwinck `std::unique_ptr oneInt = std::make_unique(0);` and `std::vector> someInts({std::move(oneInt)});` give me exactly the same error. – SaganRitual Aug 27 '17 at 14:16
  • You cannot move from a `std::initializer_list` unfortunately, so this approach simply won't work. – Kerrek SB Aug 27 '17 at 14:17
  • @KerrekSB It's not so much an approach I'm trying. I just want to use a vector of unique_ptrs. I stripped my code down to its basics just to show the problem I'm having, hoping someone could show me the proper way to do it. – SaganRitual Aug 27 '17 at 14:21
  • 2
    Just don't use initializer lists. You can populate the vector in any other way you care to. – Kerrek SB Aug 27 '17 at 14:25

2 Answers2

6

There are a lot of problems with your code. First, there is an excessive use of {} initialization that confuses things. Using auto is fine, but auto x{...}; declarations are fraught with peril, as the meaning of auto x{single_value} has shifted over time. It's best to use auto x = single_value; syntax where reasonable.

Second, you cannot insert a unique_ptr into a container through a {} initializer list. At all. Items that go through std::initializer_list must be copyable, and unique_ptr is not.

What you want is this:

auto oneInt = std::make_unique<int>(0);
std::vector<std::unique_ptr<int>> someInts;
someInts.push_back(std::move(oneInt));
Nicol Bolas
  • 449,505
  • 63
  • 781
  • 982
  • 1
    *Items that go through std::initializer_list must be copyable* -- This! – SaganRitual Aug 27 '17 at 14:23
  • Not sure, if `someInts.push_back(std::move(oneInt))` is a right thing to do. What will be inside of `oneInt` after first `move`? Nothing! – Semyon Burov Aug 27 '17 at 14:26
  • 1
    @SemyonBurov: Good idea or not, it matches the intent of the code the OP provided. – Nicol Bolas Aug 27 '17 at 14:27
  • @SemyonBurov It's ok, my actual code is a lot more complex and doesn't have the issue you mention; I was just trying to boil it down so I could get some help with `vector`. – SaganRitual Aug 27 '17 at 14:29
  • There is a rule of thumb that whenever you have `push_back(move(whatever))`, you can replace it with `emplace_back(whatever)` for convenience. Then you don't need a separate `oneInt`. – anatolyg Aug 27 '17 at 15:00
  • 2
    @anatolyg: That's a bad rule of thumb because if `whatever` is not an rvalue, it will not compile for a non-copyable type, and it will perform a copy if `whatever` is of the type `T`. – Nicol Bolas Aug 27 '17 at 15:18
2

The short answer is that a std::initializer_list contains values; and they cannot be moved out of, only copied from. To be clear, if you use the initializer_list form of construction for a vector, the items in the list are copied into the vector.

See this thread for discussion of your exact problem and some suggested workarounds.

M.M
  • 138,810
  • 21
  • 208
  • 365