8

I try to construct a vector with unique_ptr. But I do not find a direct way. The following code does not compiles. The error is:Call to implicitly-deleted copy constructor of 'std::__1::unique_ptr >':

#include <iostream>
#include <memory>
#include <utility>
#include <vector>
class test1{
public:
    test1(){};
    test1(test1&&)=default;
};

int main(int argc, const char * argv[]) {
    std::unique_ptr<test1> us(new test1());
    std::vector<std::unique_ptr<test1>> vec{move(us)};
    return 0;
}
Praetorian
  • 106,671
  • 19
  • 240
  • 328
EckhardN
  • 469
  • 5
  • 11

2 Answers2

8

You're calling the vector constructor ((7) on the linked page) that takes an initializer_list<T> argument. An initializer_list only allows const access to its elements, so the vector must copy the elements, and this, of course, fails to compile.

The following should work

std::unique_ptr<test1> us(new test1());
std::vector<std::unique_ptr<test1>> vec;

vec.push_back(move(us));
// or
vec.push_back(std::unique_ptr<test1>(new test1()));
// or
vec.push_back(std::make_unique<test1>()); // make_unique requires C++14

You could use the vector constructor that takes two iterators, but the solution is still not a one-liner because it requires you to define a temporary array that you can then move from.

std::unique_ptr<test1> arr[] = {std::make_unique<test1>()};
std::vector<std::unique_ptr<test1>> vec{std::make_move_iterator(std::begin(arr)),
                                        std::make_move_iterator(std::end(arr))};
Praetorian
  • 106,671
  • 19
  • 240
  • 328
  • In my situation, I can not use the push_back. I need a constructor because of recursion. Do I understand you right, that there is no possibility to use a constructor? – EckhardN Apr 30 '15 at 16:13
  • or `vec.emplace_back(new test1())`. – David G Apr 30 '15 at 16:13
  • vec.emplace_back also is not possible for my problem – EckhardN Apr 30 '15 at 16:14
  • 2
    @0x499602D2 Not exception safe, if `emplace_back` throws, you'll leak.\ – Praetorian Apr 30 '15 at 16:16
  • So does your example. Why not a `push_back(make_shared())`? – David G Apr 30 '15 at 16:16
  • 1
    @EckhardN I can't think of another way to construct a `vector` by transferring ownership from an existing `unique_ptr`. Can you post an example showing the problem you're facing? Maybe there's a different workaround – Praetorian Apr 30 '15 at 16:18
  • I am writing a code generator where I need to construct an object without extra code like using push_back. I depend on an constructor. – EckhardN Apr 30 '15 at 16:19
  • 2
    @0x499602D2 So does my example what? In the second case the `unique_ptr` construction is guaranteed to be complete prior to `push_back` being called. `push_back(make_shared())` won't compile, and I didn't mention `push_back(make_unique())` because the question is tagged C++11, but I'll add that anyway. – Praetorian Apr 30 '15 at 16:20
  • 1
    @EckhardN *Why* can you not add an line for a `push_back` call? Unless you add an example, it's hard for me to suggest a solution. – Praetorian Apr 30 '15 at 16:25
  • @Praetorian I am constructing a tree of objects, where I had the idea to do something like: std::vector>foo{{{unique_ptr,unique_ptr,..},{unique_ptr,unique_ptr,...}...}} – EckhardN Apr 30 '15 at 16:27
  • 1
    @EckhardN Added another option, but it probably doesn't meet your requirements either. Sorry, I don't think what you want to do is possible in that form. – Praetorian Apr 30 '15 at 16:43
  • @PraetorianThank you! Yes, a temporary array is not a possible solution. So I have to find a totally different solution. I will write n-functions with 1 to n parameters, which will construct a return the vector. Not elegant, but the best I think of. – EckhardN Apr 30 '15 at 16:52
  • @EckhardN If you have some sample code, I'm sure we can probably find a solution that doesn't involve using N overloads. – T.C. Apr 30 '15 at 17:36
6

This make_vector is a function that takes any number of arguments, and perfect-forwards them into a vector.

// get the first type in a pack, if it exists:
template<class...Ts>
struct first {};
template<class T, class...Ts>
struct first<T,Ts...>{
  using type=T;
};
template<class...Ts>
using first_t=typename first<Ts...>::type;

// build the return type:
template<class T0, class...Ts>
using vector_T = 
  typename std::conditional<
    std::is_same<T0, void>::value,
    typename std::decay<first_t<Ts...>>::type,
    T0
  >::type;
template<class T0, class...Ts>
using vector_t = std::vector< vector_T<T0, Ts...> >;

// make a vector, non-empty arg case:
template<class T0=void, class...Ts, class R=vector_t<T0, Ts...>>
R make_vector( Ts&&...ts ) {
  R retval;
  retval.reserve(sizeof...(Ts)); // we know how many elements
  // array unpacking trick:
  using discard = int[];
  (void)discard{0,((
    retval.emplace_back( std::forward<Ts>(ts) )
  ),void(),0)...};
  return retval; // NRVO!
}
// the empty overload:
template<class T>
std::vector<T> make_vector() {
  return {};
}

use:

std::vector<std::unique_ptr<test1>> vec =
  make_vector(
    std::move(u1), std::move(u2)
  );

live example

I polished it a bit. It will deduce the return type if you pass it 1 or more args and you don't pass it a type. If you pass it a type, it will use that type. If you fail to pass it a type or any args, it will complain. (if you forward packs, or are storing it in a particular type, I'd always give it a type).


A further step could be done, where we do return type deduction to eliminate the requirement to specify the type even in the empty case. This may be required in your use case, I don't know, but it matches how you don't need to specify the type of a {}, so I thought I'd toss it out there:

template<class...Ts>
struct make_vec_later {
  std::tuple<Ts...> args; // could make this `Ts&&...`, but that is scary

   // make this && in C++14
  template<class T, size_t...Is>
  std::vector<T> get(std::index_sequence<Is...>) {
    return make_vector<T>(std::get<Is>(std::move(args))...);
  }

  // make this && in C++14
  template<class T>
  operator std::vector<T>(){
    return std::move(*this).template get<T>( std::index_sequence_for<Ts...>{} );
  }
};
template<class...Ts>
make_vec_later<Ts...> v(Ts&&...ts) {
  return {std::tuple<Ts...>(std::forward<Ts>(ts)...)};
}

this does rely on a C++14 feature of index_sequence, but those are easy to rewrite in C++11 if your compiler doesn't have it yet. Simply google it on stack overflow, there are a myriad of implementations.

Now the syntax looks like:

std::vector<std::unique_ptr<test1>> vec =
  v(std::move(u1));

where the list of arguments can be empty.

Live example

Supporting variant allocators is left as an exercise to the user. Add another type to make_vector called A, and have it default to void. If it is void, swap it for std::allocator<T> for whatever type T is chosen for the vector. In the return type deduction version, do something similar.

Yakk - Adam Nevraumont
  • 262,606
  • 27
  • 330
  • 524