C++11 has introduced emplace
function to construct an element in-place inside a sequence. This is complementary to insert
which either copies or moves elements.
However, out of several overloads of insert
, only the single element insert version,
i.e.
iterator insert( const_iterator p, T const& x);
iterator insert( const_iterator p, T&& x );
has an emplace
version,
template< class... Args >
iterator emplace(const_iterator p, Args&&... x);
Is there any reason, not to allow construction of n
elements in-place using emplace
?
While a overload like,
template< class... Args >
iterator emplace(const_iterator p,size_type n,Args&&... x);
just like the corresponding insert
iterator insert(const_iterator p,size_type n,const_reference x);
may conflict with the other overload, taking the constructor arguments as a tuple
, or using some special tag like in_place_t
likely to disambiguate them.
EDIT
The proposed function emplace_n
for vector
may have behaviour like the one given below
template<class... Args>
iterator emplace_n(const_iterator p,size_type n,Args&&... x)
{
size_type const offset = p - begin();
if(capacity() < size()+n)
{
vector<T> v;
v.reserve(size()+n);
v.assign(make_move_iterator(begin(),make_move_iterator(end());
swap(*this,v);
}
auto const sz = size();
for(auto c = 0; c != n ; ++c)
{
emplace_back(x...); //can do forward only if n == 1
}
rotate(begin()+offset,begin()+sz,end());
return iterator{begin() + offset};
}
The problem is that there is no easy way to determine when the arguments for one element end and the arguments for the next begin. You can pass the arguments via tuple
s though like for pair
's piecewise constructor, and end up with a helper function like this:
template<int... Is>
struct index_seq { };
template<int N, int... Is>
struct make_index_seq : make_index_seq<N - 1, N - 1, Is...> { };
template<int... Is>
struct make_index_seq<0, Is...> : index_seq<Is...> { };
template<class Cont, class Tup, int... Is>
void emplace_back_impl(Cont& c, Tup&& tup, index_seq<Is...>)
{
using std::get;
c.emplace_back(get<Is>(std::forward<Tup>(tup))...);
}
template<class Cont, class... Tups>
void emplace_multiple(Cont& c, Tups&&... tups)
{
int const unpack[]{
0, ((emplace_back_impl)(c, std::forward<Tups>(tups),
make_index_seq<
std::tuple_size<typename std::remove_reference<Tups>::type>{}
>()), 0)...
};
static_cast<void>(unpack);
}
Then you can use the emplace_multiple
function to emplace multiple elements:
std::vector<std::string> xs;
emplace_multiple(xs, std::make_tuple("Hello, world!"), std::make_tuple(10, 'a'));
Note that this uses emplace_back
. Using emplace
plus an iterator to mark the insertion position is dangerous if you haven't reserved enough space in advance. This is because emplace
and emplace_back
can invalidate iterators (at least for vector
s), and then the function doesn't know how to get a new iterator to the position you intended.
Demo here.
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