I have a simple function which should construct a few objects and return a vector of them while also transferring the ownership. I thought the best way of doing this is simply returning a std::vector<std::unique_ptr<int>>
of the objects (let's say they are int
).
When I tried the following function:
std::vector<std::unique_ptr<int>> create_stuff() {
auto first = std::make_unique<int>(1);
auto second = std::make_unique<int>(2);
return {std::move(first), std::move(second)};
}
I was welcomed with a very very long compile error ending with:
xmemory0(737): error C2280: 'std::unique_ptr<int,std::default_delete<_Ty>>::unique_ptr(const std::unique_ptr<_Ty,std::default_delete<_Ty>> &)':
attempting to reference a deleted function
I thought the problem was with the function itself, however the following solution worked fine:
std::vector<std::unique_ptr<int>> create_stuff() {
auto first = std::make_unique<int>(1);
auto second = std::make_unique<int>(2);
std::vector<std::unique_ptr<int>> results;
results.push_back(std::move(first));
results.push_back(std::move(second));
return results;
}
Why does the second solution work but not the first one? Is there a workaround that would allow me to use the short and simple syntax with the initializer list?
Why does the second solution work but not the first one?
List initialisation syntax that you use invokes the constructor that accepts a std::initializer_list
. std::initializer_list
is not movable though, and std::initializer_list<std::unique_ptr<T>>
is not copiable, so invoking the constructor is not possible.
In the latter example you use the default constructor, so there is no problem.
Is there a workaround that would allow me to use the short and simple syntax with the initializer list?
You could list initialise an array, and use a pair of move iterators:
std::array arr{
std::make_unique<int>(1),
std::make_unique<int>(2),
};
return std::vector(
std::make_move_iterator(std::begin(arr)),
std::make_move_iterator(std::end(arr))
);
There was a proposal to make std::initializer_list
movable, but it wasn't adopted (hasn't been adopted yet; who knows what future might bring).
On my platform, the relevant code is:
vector(initializer_list<value_type> __l, const allocator_type& __a = allocator_type()) : _Base(__a) { _M_range_initialize(__l.begin(), __l.end(), random_access_iterator_tag()); }
And _M_range_initialize()
simply copies from the iterator, rather than moving-from.
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