Initialization lists are wrappers around const
arrays.
unique_ptr
s that are const
cannot be moved-from.
We can hack around this (in a perfectly legal way) like this:
template<class T>
struct movable_il {
mutable T t;
operator T() const&& { return std::move(t); }
movable_il( T&& in ): t(std::move(in)) {}
};
template<class T, class A=std::allocator<T>>
std::vector<T,A> vector_from_il( std::initializer_list< movable_il<T> > il ) {
std::vector<T,A> r( std::make_move_iterator(il.begin()), std::make_move_iterator(il.end()) );
return r;
}
Live example.
Use:
auto v = vector_from_il< std::unique_ptr<int> >({
std::make_unique<int>(7),
std::make_unique<int>(3)
});
If you want to know why initializer lists reference const data, you'll have to track down and read committee minutes or ask someone who was there. I'd guess it is about the principle of least surprise and/or people with bugaboos about mutable data and view types (such as the renaming of array_view
to span
).
If you want more than just vectors:
template<class C, class T=typename C::value_type>
C container_from_il( std::initializer_list< movable_il<T> > il ) {
C r( std::make_move_iterator(il.begin()), std::make_move_iterator(il.end()) );
return r;
}
which still needs massaging to work right with associative containers as we also want to move the key.
template<class VT>
struct fix_vt {
using type=VT;
};
template<class VT>
using fix_vt_t = typename fix_vt<VT>::type;
template<class VT>
struct fix_vt<const VT>:fix_vt<VT>{};
template<class K, class V>
struct fix_vt<std::pair<K,V>>{
using type=std::pair<
typename std::remove_cv<K>::type,
typename std::remove_cv<V>::type
>;
};
template<class C, class T=fix_vt_t<typename C::value_type>>
C container_from_il( std::initializer_list< movable_il<T> > il ) {
C r( std::make_move_iterator(il.begin()), std::make_move_iterator(il.end()) );
return r;
}
与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…