[ACCEPTED]-Using emplace with algorithms such as std::fill-stl-algorithm

Accepted answer
Score: 15

It's common to use tuples to ease the pass 17 a variadic number of items (in this case, parameters 16 to forward to emplace_back), with a little technique to unpack the tuple 15 back. As such it is possible to write a 14 back_emplacer utility by requiring the user to make use 13 of the tuple factory functions (one of std::make_tuple, std::tie, std::forward_as_tuple) where 12 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 11 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 10 also acceptable). You'd also want the usual 9 iterator stuff to make the feature complete 8 -- I recommend Boost.Iterators for that.

I 7 must really stress however that such a utility 6 doesn't bring much when used with std::fill_n. In your 5 case it would save the construction of the 4 temporary Foo, in favour of a tuple of references 3 (a tuple of values if you were to use std::make_tuple). I 2 leave it to the reader to find some other 1 algorithm where back_emplacer would be useful.

Score: 10

You are right that there is no back_emplacer in the standard. You 18 could perfectly write one yourself, but 17 what for ?

When you call emplace_back, you have to provide 16 the arguments for the constructor (any constructor): vec.emplace_back(1, 2) for 15 example. However, you cannot arbitrarily 14 pass tuples of arguments in C++, so the 13 back_emplacer would be limited to unary constructor.

In 12 the case of fill_n, you provide an argument that 11 will be copied, and then both back_inserter and back_emplacer would call 10 the same copy constructor with the same 9 argument.

Note that there is the generate and generate_n algorithms 8 to build new elements. But likewise any 7 temporary copy will probably be elided.

Therefore 6 I think the need for a back_emplacer is rather light, mostly 5 because of the language non-support of multiple 4 return values.

EDIT

If you look at the comments 3 below you will realize that using a combination 2 std::forward_as_tuple and std::is_constructible it could be possible to write a back_emplacer mechanism. Thanks 1 to Luc Danton for the breakthrough.

Score: 6
class Foo {
public:
  Foo(int i, double d) : i_(i), d_(d) {}
};

std::vector<Foo> v;
v.reserve(10);
std::generate_n(std::back_inserter(v), 10, [&]()->Foo{ return {1, 1.0}; });

RVO allows the return value of a function 7 to be elided directly into where it is going 6 to be stored.

While logically a temporary 5 is created, in actual fact no temporary 4 is created. And you have access to all 3 variables in the surrounding scope to decide 2 how to create the element, not just constants, if 1 you want them.

Score: 2

There won't be any "temporal copies" made. There 6 will be exactly one temporary, the one you 5 passed to fill_n. And it will be copied into each 4 value.

And even if there was a back_emplacer, what would 3 you call it with? The emplace familiy of functions 2 take constructor parameters; fill_n takes an object to 1 copy into the iterator.

Score: 0

I recently submitted an emplace_iterator class and related 10 utility function to the folly library. I 9 believe it solves the original question 8 and supports automatic unzipping of std::tuple arguments 7 passed to operator=.

Edit: Updated Link: https://github.com/facebook/folly/blob/master/folly/container/Iterator.h

class Widget { Widget(int, int); };

std::vector<Widget> makeWidgets(const std::vector<int>& in) {
  std::vector<Widget> out;
  std::transform(
      in.begin(),
      in.end(),
      folly::back_emplacer(out),
      [](int i) { return folly::make_emplace_args(i, i); });
  return out;
}

folly::make_emplace_args is analogous 6 to std::make_tuple but results in perfect forwarding of 5 its arguments to the Widget constructor. (std::make_tuple and 4 similar may result in additional copies 3 and does not preserve lvalue vs rvalue typedness.) In 2 this specific example, using std::make_tuple would have 1 the same effect though.

Score: 0

I've seen @LucDanton's answer above (https://stackoverflow.com/a/12131700/1032917) and 8 I still cannot see the point of making the 7 code overly complicated (apart from the 6 fact it was written back in 2012, but even 5 given that...). Anyway, I find the following 4 code as functional as Luc's:

template <typename Container>
class back_emplace_iterator
{
public:
    explicit back_emplace_iterator(Container & container)
        : container(std::addressof(container))
    {}

    template <typename... Args>
    back_emplace_iterator & operator=(Args &&... args)
    {
        static_assert(std::is_constructible_v<typename Container::value_type, Args...>, "should be constructible");

        assert(container);
        container->emplace_back(std::forward<Args>(args)...);

        return *this;
    }

    // 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};
}

And with CTAD 3 in C++17, you can even get rid of back_emplacer and write 2 back_emplace_iterator(my_container) without explicitly giving the template 1 arguments.

More Related questions