tags:

views:

96

answers:

4

Maybe I am missing something completely obvious, but I can't figure out why one would use back_inserter/front_inserter/inserter, instead of just providing the appropriate iterator from the container interface. And thats my question.

+4  A: 

Because those call push_back, push_front, and insert which the container's "normal" iterators can't (or, at least, don't).

Example:

int main() {
  using namespace std;
  vector<int> a (3, 42), b;

  copy(a.begin(), a.end(), back_inserter(b));

  copy(b.rbegin(), b.rend(), ostream_iterator<int>(cout, ", "));
  return 0;
}
Roger Pate
David Rodríguez - dribeas
It does work as expected, the standard requires this and points out there are multiple ways to implement it to do this. Also you can run the code yourself, all I left out were #includes. :) Use http://comeaucomputing.com/tryitout which is fully compliant (does not link, has -tused to help find errors even so).
Roger Pate
@David Rodríguez: It does call the templated version, but the C++ standard explcitly requires (23.1.1/9) that the templated version's functionality for an integral type be the same as that "size, initial value" one. How the implementation is going to achieve that is its own business.
AndreyT
I think both of you are right: actually the InputIterator version is called, but this is itself redirected via tag dispatching to an internal _Construct member function that interprets the two iterators as size and initial value (checked on MSVC 2008). I guess this is what Roger means with "ways to implement this".
Manuel
@Manuel: You can also do a metric ton of ctor overloading. There may be more ways I don't remember.
Roger Pate
Good, and thanks @AndreyT for the standard reference.
David Rodríguez - dribeas
+2  A: 

The main reason is that regular iterators iterate over existing elements in the container, while the *inserter family of iterators actually inserts new elements in the container.

std::vector<int> v(3);  // { 0, 0, 0 }
int array[] = { 1, 2, 3 };
std::copy( array, array+3, std::back_inserter(v) ); // adds 3 elements
   // v = { 0, 0, 0, 1, 2, 3 }
std::copy( array, array+3, v.begin() ); // overwrites 3 elements
   // v = { 1, 2, 3, 1, 2, 3 } 
int array2[] = { 4, 5, 6 };
std::copy( array2, array2+3, std::inserter(v, v.begin()) );
   // v = { 4, 5, 6, 1, 2, 3, 1, 2, 3 }
David Rodríguez - dribeas
A: 

The iterator points to an element, and doesn't in general know what container it's attached to. (Iterators were based on pointers, and you can't tell from a pointer what data structure it's associated with.)

Adding an element to an STL container changes the description of the container. For example, STL containers have a .size() function, which has to change. Since some metadata has to change, whatever inserts the new element has to know what container it's adding to.

David Thornley
A: 

It is all a matter of what you really need. Both kinds of iterators can be used, depending on your intent.

When you use an "ordinary" iterator, it does not create new elements in the container. It simply writes the data into the existing consecutive elements of the container, one after another. It overwrites any data that is already in the container. And if it is allowed to reach the end of the sequence, any further writes make it "fall off the end" and cause undefined behavior. I.e. it crashes.

Inserter iterators, on the other hand, create a new element and insert it at the current position (front, back, somewhere in the middle) every time something is written through them. They never overwrite existing elements, they add new ones.

AndreyT