57

C++98 has front_inserter, back_inserter, and inserter, but there don't seem to be any emplacement versions of these in C++11 or draft C++14. Is there any technical reason we couldn't have front_emplacer, back_emplacer, and emplacer?

templatetypedef
  • 362,284
  • 104
  • 897
  • 1,065
KnowItAllWannabe
  • 12,972
  • 8
  • 50
  • 91
  • 5
    What are you going to assign into them? Tuples of arguments? – Pavel Anossov Sep 10 '13 at 17:13
  • @PavelAnossov: It would allow conversions constructors at least `*front_inserter(string_vector) = "hello world";` – Mooing Duck Sep 10 '13 at 17:17
  • 1
    @MooingDuck: that already works with inserters – Pavel Anossov Sep 10 '13 at 17:21
  • 3
    @PavelAnossov: But because the inserters insist on inserting an object of the type held by the container, you pay a penalty (typically a move) for inserting a different type. Emplacement would avoid that penalty. – KnowItAllWannabe Sep 10 '13 at 17:22
  • Can you give a sensible use case? – Walter Sep 10 '13 at 17:30
  • 4
    @Walter: Use Case 1: I have a container of ints and a container of objects, where each object can be initialized with an int. I want to append to the container of objects a sequence of new objects initialized with the ints, and I want to do it maximally efficiently. Use case 2: I have a library of algorithms in the STL style, but they operate on multiple input sequences at once (like the two-sequence version of std::transform). I want to add new objects to the front of a sequence, where the new objects' n constructor arguments are taken from the n input sequences I'm processing. – KnowItAllWannabe Sep 10 '13 at 17:39
  • 2
    @Walter: inserting into an array of objects where the objects are large and/or legacy types that are copiable but not movable . It's not exactly a stretch. – Mooing Duck Sep 10 '13 at 17:48
  • @KnowItAllWannabe The problem is that `back_emplacer`, when used with algorithms, would accept only a single constructor argument (such as your `int`). Algorithms such as `std::generate` do no provide possibility to pass multiple arguments to be forwarded to `back_emplacer`. – Daniel Langr Sep 06 '19 at 07:12

2 Answers2

35

Is there any technical reason we couldn't have front_emplacer, back_emplacer, and emplacer?

No, there is no technical reason. As proof, here is a complete implementation of back_emplacer with a demo of your Use Case 1...

#include <iterator>
#include <vector>
#include <iostream>

template<class Container>
class back_emplace_iterator : public std::iterator< std::output_iterator_tag,
                                                   void, void, void, void >
{
protected:
    Container* container;
public:
    typedef Container container_type;

    explicit back_emplace_iterator(Container& x) : container(&x) {}

    template<class T>
    back_emplace_iterator<Container>&
    operator=(T&& t)
    {
        container->emplace_back(std::forward<T>(t));
        return *this;
    }

    back_emplace_iterator& operator*() { return *this; }
    back_emplace_iterator& operator++() { return *this; }
    back_emplace_iterator& operator++(int) { return *this; }
};

template< class Container >
inline back_emplace_iterator<Container>
back_emplacer( Container& c )
{
    return back_emplace_iterator<Container>(c);
}

struct Demo
{
    int i;
    Demo(int i) : i(i) {}
};

int main()
{
    std::vector<int> x = {1,2,3,4,5};

    std::vector<Demo> y;

    std::copy(x.begin(), x.end(), back_emplacer(y));

    for (auto d : y)
        std::cout << d.i << std::endl;
}

Possible Known Issue: Does the universal reference of operator= hide an implicitly generated copy/move operator=? If so these need to be explicitly defined in a way that beats the universal reference in overload resolution.

gsamaras
  • 71,951
  • 46
  • 188
  • 305
Andrew Tomazos
  • 66,139
  • 40
  • 186
  • 319
  • I have [asked another question](http://stackoverflow.com/questions/18728257/back-emplacer-implementation-default-operator) regarding the known issue. – Andrew Tomazos Sep 10 '13 at 20:33
  • @MooingDuck: Proxy class for what? Go have a look at the libstdc++ implementation of `back_inserter`. It is in `bits/stl_iterator.h`. – Andrew Tomazos Sep 10 '13 at 20:50
  • @MooingDuck: So it is required by the C++ standard to not use a proxy class, but to implement it the way shown. See N3485 24.5.2.2.3. – Andrew Tomazos Sep 10 '13 at 21:25
  • Existence proofs are awesome :-) I'm marking this as the answer. Thanks for the great work! – KnowItAllWannabe Sep 10 '13 at 22:17
  • @user1131467: Wow, so it is. That's strange to me, I wonder why they decided to do it that way? – Mooing Duck Sep 10 '13 at 22:48
  • 1
    Is the advantage of this over plain `back_inserter_iterator`? https://stackoverflow.com/questions/54303852/is-there-a-back-inserter-variant-that-takes-advantage-of-move/54304086#54304086 – alfC Jan 22 '19 at 08:46
  • 1
    @alfC: Yeah I now think the notion of `back_emplace_iterator` is conceptually flawed. "emplace"-style operations take a pack of arguments and then forward them to the constructor. See for example `std::vector::emplace_back`. Such an operation does not fit into the iterator paradigm, because the underlying iterator abstraction does not support it. – Andrew Tomazos Jul 13 '21 at 22:04
7

Your main use case is already covered by inserter, back_inserter and front_inserter. There is already a value_type && overload of operator= that will move into the container. The only thing emplacer could do over inserter is call explicit constructors.

Compare the common overloads of container::insert, container::push_back and container::push_front to container::emplace, container::emplace_back and container::emplace_front

iterator insert( const_iterator pos, const value_type & value );
iterator insert( const_iterator pos, value_type && value );

template< class... Args > 
iterator emplace( const_iterator pos, Args&&... args );

void push_back( const value_type & value );
void push_back( value_type && value );

template< class... Args >
void emplace_back( Args&&... args );

void push_front( const value_type & value );
void push_front( value_type && value );

template< class... Args >
void emplace_front( Args&&... args );

Each of the emplace variants takes a pack of arguments with which to construct the value. operator = takes exactly one argument. You could write an emplacer that took a tuple of arguments.

template<class Container>
class back_emplace_iterator : public std::iterator< std::output_iterator_tag,
                                                   void, void, void, void >
{
protected:
    Container* container;
public:
    typedef Container container_type;

    explicit back_emplace_iterator(Container& x) : container(&x) {}

    template<typename ... Args>
    back_emplace_iterator<Container>&
    operator=(std::tuple<Args&&...> args)
    {
        std::apply(Container::emplace_back, std::tuple_cat(std::tie(*container), std::forward<std::tuple<Args&&...>>(args)));
        return *this;
    }

    back_emplace_iterator& operator*() { return *this; }
    back_emplace_iterator& operator++() { return *this; }
    back_emplace_iterator& operator++(int) { return *this; }
};
Caleth
  • 52,200
  • 2
  • 44
  • 75