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;
}
You can store pointers in a vector just like you would anything else. Declare a vector of pointers like this: vector<MyClass*> vec; The important thing to remember is that a vector stores values without regard for what those values represent.
An ordinary vector encountered in C++ programming, is a vector of objects of the same type. These objects can be fundamental objects or objects instantiated from a class. This article illustrates examples of vector of pointers, to same object type.
A unique_ptr can only be moved. This means that the ownership of the memory resource is transferred to another unique_ptr and the original unique_ptr no longer owns it. We recommend that you restrict an object to one owner, because multiple ownership adds complexity to the program logic.
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))};
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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With