in-place vector construction from initialization list (for class with constructor arguments)
c++, c++11, initialization, initializer-list
Solution
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`.
Problem
Possible Duplicate: Can I list-initialize a vector of move-only type? Edit 1: Please consider a re-open vote: My question emphasize in-place construction. Move construction is an alternative but not what this questions is about. Thanks for the answers! Edit 2: Since I can't answer this question (It got closed) I post my own suggestion here. The following is not as good as the answers I accepted, but may be useful for others. At least only the move constructor is called: ``` std::vector<A2> vec; { std::array<A2,3> numbers{{{2,3},{5,6},{7,8}}}; vec.reserve(numbers.size()); for (auto &v: numbers) vec.emplace_back(std::move(v)) ; } ``` Original post: When thinking about the answer to this question: Initialization of classes within an STL array of vectors I found that I could not find a way to get in-place construction of the vector from an initialization list. What am I missing? Now trying to be more clear, I would like this (perfectly correct) initialization ``` std::vector<A2> k{{2,3},{4,5},{8,9}}; ``` to have an effect more similar to this: ``` std::vector<A2> k2; k2.reserve(3); k2.emplace_back(2,3); k2.emplace_back(4,5); k2.emplace_back(8,9); ``` However, in the first case the copy constructor is called for A2 on a temporary while inserting. Is there a way to avoid that? What does the standard say? I desperately tried ``` std::vector<A2> k{{2,3},{4,5},std::move(A2{8,9})}; ``` but that generates an additional call to the move constructor, something I also did not expect. I just wanted to explicitly hint that A2 is a temporary, something I had thought was implied. Full example: ``` #include <vector> #include <iostream> struct A2 { int mk; int mj; A2(int k,int j) : mk(k),mj(j) { std::cout << " constr for "<<this<< ":"<< mk<<std::endl; } A2(const A2& a2) { mk=a2.mk; mj=a2.mj; std::cout << "copy constr for "<<this<< ":" << mk<<std::endl; } A2(A2&& a2) noexcept { mk=std::move(a2.mk); mj=std::move(a2.mj); std::cout << "move constr for "<<this<< ":"<< mk<<std::endl; } }; struct Ano { Ano() { std::cout << " constr for "<<this <<std::endl; } Ano(const Ano& ano) { std::cout << "copy constr for "<<this<<std::endl; } Ano(Ano&& ano) noexcept { std::cout << "move constr for "<<this<<std::endl; } }; int main (){ // here both constructor and copy constructor is called: std::vector<A2> k{{2,3},{4,5},std::move(A2{8,9})}; std::cout << "......"<<std::endl; std::vector<A2> k2; k2.reserve(3); // here (naturally) only constructor is called: k2.emplace_back(2,3); k2.emplace_back(4,5); k2.emplace_back(8,9); std::cout << "......"<<std::endl; // here only constructor is called: std::vector<Ano> anos(3); } ``` Output: ``` constr for 0xbf9fdf18:2 constr for 0xbf9fdf20:4 constr for 0xbf9fdf0c:8 move constr for 0xbf9fdf28:8 copy constr for 0x90ed008:2 copy constr for 0x90ed010:4 copy constr for 0x90ed018:8 ...... constr for 0x90ed028:2 constr for 0x90ed030:4 constr for 0x90ed038:8 ...... constr for 0x90ed048 constr for 0x90ed049 constr for 0x90ed04a ```