List-initializing std::vector
in your snippet is no different from doing the following (if initializer_list
had a public non-explicit constructor or std::vector
accepted an array reference.):
// directly construct with the backing array of 'initializer_list'
std::vector<A2> v(alias<A2[]>{ A2(2,3), A2(4,5), A2(8,9) });
It's not a special way to construct a std::vector
that could take advantage of the implementation, really. List-initialization is a general-purpose way of "uniformly" initializing types. As such, there's no way it could tread std::vector
any different than any other user-defined type. As such, having the construct in the OP do emplace construction is out of question.
Now, the backing array (or any constant array) may be put in read-only memory by the implementation, that's the reason why
std::initializer_list<T>::iterator
is just
typedef T const* iterator;
So moving out of std::initializer_list
is also out of question.
Now, is there a solution? Yes, there is, and it's a rather easy one, actually!
We will want to have a free function that takes a container and a number of tuples equal to the number of elements you want to emplace. The tuples container the arguments to the constructor of the container type. Easy in theory, easy in practice with the indices trick (where indices == seq
and build_indices == gen_seq
in the code):
#include <type_traits>
#include <tuple>
#include <utility>
template<class T> using alias = T;
template<class T> using RemoveRef = typename std::remove_reference<T>::type;
template<class C, unsigned... Is, class Tuple>
void emplace_back_one(C& c, seq<Is...>, Tuple&& ts){
c.emplace_back(std::get<Is>(std::forward<Tuple>(ts))...);
}
template<class T> using Size = std::tuple_size<RemoveRef<T>>;
template<class C, class... Tuples>
void emplace_back(C& c, Tuples&&... ts){
c.reserve(sizeof...(Tuples));
alias<char[]>{(
emplace_back_one(c, gen_seq<std::tuple_size<RemoveRef<Tuples>>::value>{}, std::forward<Tuples>(ts))
, '0')...};
}
Live example with the implementation of seq and gen_seq.
The code above calls emplace_back_one
exactly sizeof...(Tuples)
times, passing one tuple at a time in the order that the were passed to emplace_back
. This code is also sequenced left-to-right, meaning that the constructors get called in the same order that you passed the tuples for them. emplace_back_one
then simply unpacks the tuple with the indices trick and passes the arguments to c.emplace_back
.