Can I list-initialize a vector of move-only type?
Solution 1:
Edit: Since @Johannes doesn't seem to want to post the best solution as an answer, I'll just do it.
#include <iterator>
#include <vector>
#include <memory>
int main(){
using move_only = std::unique_ptr<int>;
move_only init[] = { move_only(), move_only(), move_only() };
std::vector<move_only> v{std::make_move_iterator(std::begin(init)),
std::make_move_iterator(std::end(init))};
}
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:
#include <utility>
#include <type_traits>
template<class T>
struct rref_wrapper
{ // CAUTION - very volatile, use with care
explicit rref_wrapper(T&& v)
: _val(std::move(v)) {}
explicit operator T() const{
return T{ std::move(_val) };
}
private:
T&& _val;
};
// only usable on temporaries
template<class T>
typename std::enable_if<
!std::is_lvalue_reference<T>::value,
rref_wrapper<T>
>::type rref(T&& v){
return rref_wrapper<T>(std::move(v));
}
// lvalue reference can go away
template<class T>
void rref(T&) = delete;
Sadly, the straight-forward code here won't work:
std::vector<move_only> v{ rref(move_only()), rref(move_only()), rref(move_only()) };
Since the standard, for whatever reason, doesn't define a converting copy constructor like this:
// in class initializer_list
template<class U>
initializer_list(initializer_list<U> const& other);
The initializer_list<rref_wrapper<move_only>>
created by the brace-init-list ({...}
) won't convert to the initializer_list<move_only>
that the vector<move_only>
takes. So we need a two-step initialization here:
std::initializer_list<rref_wrapper<move_only>> il{ rref(move_only()),
rref(move_only()),
rref(move_only()) };
std::vector<move_only> v(il.begin(), il.end());
Solution 2:
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:
typedef const E& reference;
typedef const E& const_reference;
typedef const E* iterator;
typedef const E* const_iterator;
const E* begin() const noexcept; // first element
const E* end() const noexcept; // one past the last element
Solution 3:
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:
#include <vector>
#include <memory>
struct Foo
{
std::unique_ptr<int> u;
int x;
Foo(int x = 0): x(x) {}
};
template<typename V> // recursion-ender
void multi_emplace(std::vector<V> &vec) {}
template<typename V, typename T1, typename... Types>
void multi_emplace(std::vector<V> &vec, T1&& t1, Types&&... args)
{
vec.emplace_back( std::move(t1) );
multi_emplace(vec, args...);
}
int main()
{
std::vector<Foo> foos;
multi_emplace(foos, 1, 2, 3, 4, 5);
multi_emplace(foos, Foo{}, Foo{});
}
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 use vector::resize
)
Solution 4:
Update for C++20:
Using Johannes Schaub's trick of std::make_move_iterator()
with C++20's std::to_array()
, you can use a helper function like unto make_tuple()
etc., here called make_vector()
:
#include <array>
#include <memory>
#include <vector>
struct X {};
template<class T, std::size_t N>
auto make_vector( std::array<T,N>&& a )
-> std::vector<T>
{
return { std::make_move_iterator(std::begin(a)), std::make_move_iterator(std::end(a)) };
}
template<class... T>
auto make_vector( T&& ... t )
{
return make_vector( std::to_array({ std::forward<T>(t)... }) );
}
int main()
{
using UX = std::unique_ptr<X>;
const auto a = std::to_array({ UX{}, UX{}, UX{} }); // Ok
const auto v0 = make_vector( UX{}, UX{}, UX{} ); // Ok
//const auto v2 = std::vector< UX >{ UX{}, UX{}, UX{} }; // !! Error !!
}
See it live on Godbolt.
Similar answer for older C++:
Using Johannes Schaub's trick of std::make_move_iterator()
with std::experimental::make_array()
, you can use a helper function:
#include <memory>
#include <type_traits>
#include <vector>
#include <experimental/array>
struct X {};
template<class T, std::size_t N>
auto make_vector( std::array<T,N>&& a )
-> std::vector<T>
{
return { std::make_move_iterator(std::begin(a)), std::make_move_iterator(std::end(a)) };
}
template<class... T>
auto make_vector( T&& ... t )
-> std::vector<typename std::common_type<T...>::type>
{
return make_vector( std::experimental::make_array( std::forward<T>(t)... ) );
}
int main()
{
using UX = std::unique_ptr<X>;
const auto a = std::experimental::make_array( UX{}, UX{}, UX{} ); // Ok
const auto v0 = make_vector( UX{}, UX{}, UX{} ); // Ok
//const auto v1 = std::vector< UX >{ UX{}, UX{}, UX{} }; // !! Error !!
}
See it live on Coliru.
Perhaps someone can leverage std::make_array()
's trickery to allow make_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.