Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why no emplacement iterators in C++11 or C++14?

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?

like image 237
KnowItAllWannabe Avatar asked Sep 10 '13 17:09

KnowItAllWannabe


People also ask

Why do we need iterators in C++?

Iterators are used to traverse from one element to another element, a process is known as iterating through the container. The main advantage of an iterator is to provide a common interface for all the containers type. Iterators make the algorithm independent of the type of the container used.

Do stacks have iterators?

Stack does not have iterators, by definition of stack. If you need stack with iterators, you'll need to implement it yourself on top of other container (std::list, std::vector, etc).

Are iterators part of STL?

Iterators are one of the four pillars of the Standard Template Library or STL in C++. An iterator is used to point to the memory address of the STL container classes.

How do STL iterators work?

An iterator is an object that can navigate over elements of STL containers. All iterator represents a certain position in a container. To make it work, iterators have the following basic operations which are exactly the interface of ordinary pointers when they are used to iterator over the elements of an array.


2 Answers

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.

like image 127
Andrew Tomazos Avatar answered Sep 22 '22 00:09

Andrew Tomazos


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; } }; 
like image 35
Caleth Avatar answered Sep 24 '22 00:09

Caleth