I have used vector::emplace_back
in order to avoid constructing temporal objects while filling a vector. Here you have a simplified version:
class Foo {
public:
Foo(int i, double d) : i_(i), d_(d) {}
/* ... */
};
std::vector<Foo> v;
v.reserve(10);
for (int i = 0; i < 10; i++)
v.emplace_back(1, 1.0);
But I wanted to use std::fill_n
instead:
v.reserve(10);
std::fill_n(std::back_inserter(v), 10, Foo(1, 1.0));
In this way, temporal copies will be created, though. I do not know how to use emplace
in this situation. I guess I would need something like std::back_emplacer
, but I could not find such a thing. Is that part of C++11, but not implemented in GCC yet? If it is not part of C++11, is there any other way to do that?
C++ Vector Library - emplace() Function The C++ function std::vector::emplace() extends container by inserting new element at position. Reallocation happens if there is need of more space. This method increases container size by one.
std::transform applies the given function to a range and stores the result in another range, keeping the original elements order and beginning at d_first .
push_back: Adds a new element at the end of the container, after its current last element. The content of val is copied (or moved) to the new element. emplace_back: Inserts a new element at the end of the container, right after its current last element.
It's common to use tuples to ease the pass a variadic number of items (in this case, parameters to forward to emplace_back
), with a little technique to unpack the tuple back. As such it is possible to write a back_emplacer
utility by requiring the user to make use of the tuple factory functions (one of std::make_tuple
, std::tie
, std::forward_as_tuple
) where it make sense:
#include <type_traits>
#include <tuple>
// Reusable utilites
template<typename T>
using RemoveReference = typename std::remove_reference<T>::type;
template<typename T>
using Bare = typename std::remove_cv<RemoveReference<T>>::type;
template<typename Out, typename In>
using WithValueCategoryOf = typename std::conditional<
std::is_lvalue_reference<In>::value
, typename std::add_lvalue_reference<Out>::type
, typename std::conditional<
std::is_rvalue_reference<Out>::value
, typename std::add_rvalue_reference<Out>::type
, Out
>::type
>::type;
template<int N, typename Tuple>
using TupleElement = WithValueCategoryOf<
typename std::tuple_element<N, RemoveReference<Tuple>>::type
, Tuple
>;
// Utilities to unpack a tuple
template<int... N>
struct indices {
using next = indices<N..., sizeof...(N)>;
};
template<int N>
struct build_indices {
using type = typename build_indices<N - 1>::type::next;
};
template<>
struct build_indices<0> {
using type = indices<>;
};
template<typename Tuple>
constexpr
typename build_indices<std::tuple_size<Bare<Tuple>>::value>::type
make_indices() { return {}; }
template<typename Container>
class back_emplace_iterator {
public:
explicit back_emplace_iterator(Container& container)
: container(&container)
{}
template<
typename Tuple
// It's important that a member like operator= be constrained
// in this case the constraint is delegated to emplace,
// where it can more easily be expressed (by expanding the tuple)
, typename = decltype( emplace(std::declval<Tuple>(), make_indices<Tuple>()) )
>
back_emplace_iterator& operator=(Tuple&& tuple)
{
emplace(*container, std::forward<Tuple>(tuple), make_indices<Tuple>());
return *this;
}
template<
typename Tuple
, int... Indices
, typename std::enable_if<
std::is_constructible<
typename Container::value_type
, TupleElement<Indices, Tuple>...
>::value
, int
>::type...
>
void emplace(Tuple&& tuple, indices<Indices...>)
{
using std::get;
container->emplace_back(get<Indices>(std::forward<Tuple>(tuple))...);
}
// Mimic interface of std::back_insert_iterator
back_emplace_iterator& operator*() { return *this; }
back_emplace_iterator& operator++() { return *this; }
back_emplace_iterator operator++(int) { return *this; }
private:
Container* container;
};
template<typename Container>
back_emplace_iterator<Container> back_emplacer(Container& c)
{ return back_emplace_iterator<Container> { c }; }
A demonstration of the code is available. In your case you'd want to call std::fill_n(back_emplacer(v), 10, std::forward_as_tuple(1, 1.0));
(std::make_tuple
is also acceptable). You'd also want the usual iterator stuff to make the feature complete -- I recommend Boost.Iterators for that.
I must really stress however that such a utility doesn't bring much when used with std::fill_n
. In your case it would save the construction of the temporary Foo
, in favour of a tuple of references (a tuple of values if you were to use std::make_tuple
). I leave it to the reader to find some other algorithm where back_emplacer
would be useful.
You are right that there is no back_emplacer
in the standard. You could perfectly write one yourself, but what for ?
When you call emplace_back
, you have to provide the arguments for the constructor (any constructor): vec.emplace_back(1, 2)
for example. However, you cannot arbitrarily pass tuples of arguments in C++, so the back_emplacer
would be limited to unary constructor.
In the case of fill_n
, you provide an argument that will be copied, and then both back_inserter
and back_emplacer
would call the same copy constructor with the same argument.
Note that there is the generate
and generate_n
algorithms to build new elements. But likewise any temporary copy will probably be elided.
Therefore I think the need for a back_emplacer
is rather light, mostly because of the language non-support of multiple return values.
EDIT
If you look at the comments below you will realize that using a combination std::forward_as_tuple
and std::is_constructible
it could be possible to write a back_emplacer
mechanism. Thanks to Luc Danton for the breakthrough.
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