If I pass the following code through my GCC 4.7 snapshot, it tries to copy the unique_ptr
s into the vector.
#include <vector>
#include <memory>
int main() {
using move_only = std::unique_ptr<int>;
std::vector<move_only> v { move_only(), move_only(), move_only() };
}
Obviously that cannot work because std::unique_ptr
is not copyable:
error: use of deleted function 'std::unique_ptr<_Tp, _Dp>::unique_ptr(const std::unique_ptr<_Tp, _Dp>&) [with _Tp = int; _Dp = std::default_delete; std::unique_ptr<_Tp, _Dp> = std::unique_ptr]'
Is GCC correct in trying to copy the pointers from the initializer list?
As mentioned in other answers, the behaviour of
std::initializer_list
is to hold objects by value and not allow moving out, so this is not possible. Here is one possible workaround, using a function call where the initializers are given as variadic arguments:Unfortunately
multi_emplace(foos, {});
fails as it cannot deduce the type for{}
, so for objects to be default-constructed you have to repeat the class name. (or usevector::resize
)As it has been pointed out, it is not possible to initialize a vector of move-only type with an initializer list. The solution originally proposed by @Johannes works fine, but I have another idea... What if we don't create a temporary array and then move elements from there into the vector, but use placement
new
to initialize this array already in place of the vector's memory block?Here's my function to initialize a vector of
unique_ptr
's using an argument pack:Using Johannes Schaub's trick of
std::make_move_iterator()
withstd::experimental::make_array()
, you can use a helper function:See it live on Coliru.
Perhaps someone can leverage
std::make_array()
's trickery to allowmake_vector()
to do its thing directly, but I did not see how (more accurately, I tried what I thought should work, failed, and moved on). In any case, the compiler should be able to inline the array to vector transformation, as Clang does with O2 on GodBolt.The synopsis of
<initializer_list>
in 18.9 makes it reasonably clear that elements of an initializer list are always passed via const-reference. Unfortunately, there does not appear to be any way of using move-semantic in initializer list elements in the current revision of the language.Specifically, we have:
Edit: Since @Johannes doesn't seem to want to post the best solution as an answer, I'll just do it.
The iterators returned by
std::make_move_iterator
will move the pointed-to element when being dereferenced.Original answer: We're gonna utilize a little helper type here:
Sadly, the straight-forward code here won't work:
Since the standard, for whatever reason, doesn't define a converting copy constructor like this:
The
initializer_list<rref_wrapper<move_only>>
created by the brace-init-list ({...}
) won't convert to theinitializer_list<move_only>
that thevector<move_only>
takes. So we need a two-step initialization here: