Reputation: 880
If random access iterators can be used to access elements at an arbitrary offset position relative to element they point to (somehow like pointers), why can't they be used in generic algorithms like std::copy()
instead of using back_insert_iterator
, and what's the difference between both?
Upvotes: 6
Views: 4327
Reputation: 361352
std::back_insert_iterator
is a specific kind of output iterator which supports push_back
operation. When you write to it using operator=
, it push_backs the value into the underlying container — so, in that sense it acts as an adaptor for containers which has push_back
member function.
An example would be easy to understand:
std::vector<int> v;
std::back_insert_iterator<std::vector<int>> it(v);
*it = 10; // it is equivalent to v.push_back(10);
it = 99; // it is ALSO equivalent to v.push_back(99);
for (auto const & i : v)
std::cout << i << " " ; //10 99
It outputs :
10 99
The usual iterator operations ++
and *
on it
has no effect.
But you rarely use them directly (I've never used it directly untill now). You use them with algorithms, such as std::copy
in which case you also use std::back_inserter
function which returns an object of type std::back_insert_iterator
.
//assuming dest is a container which supports push_back!
std::copy(src.begin(), src.end(), std::back_inserter(dest));
You would also like to see the following (adaptor) iterators:
push_front
operationinsert
operation.So depending on the container, you choose adaptor iterator.
Note that all of them are output iterator.
why can't they be used in generic algorithms like std::copy() instead of using back_insert_iterator.
Of course, you can use the random access iterators (or any output iterator) in algorithms like std::copy
, as third argument, but that assumes the iterator is referencing to existing range — *it
and ++it
are well-defined for the value you passed. You pass them to overwrite the existing elements of the range, whereas std::back_insert_iterator
adds new elements to the container.
Hope that helps.
Upvotes: 17
Reputation: 299780
Actually, you can totally use a regular iterator in std::copy
.
int main() {
std::vector<int> vec{1, 2, 3, 4};
std::list<int> list{vec.size()};
std::copy(vec.begin(), vec.end(), list.begin());
// list = 1, 2, 3, 4
}
However, as you may note, it means that:
len(source range)
default elementswhich is rather inefficient, and requires that elements can be default constructed and then assigned to.
Instead, back_insert_iterator
is a fake iterators, which operates as an Adaptor over a regular container. If you look at the interface you will see that it does not behave as a regular iterator at all, and just calls push_back
on the underlying container reference it embeds whenever you try to push an item in.
int main() {
std::list<int> list;
std::back_insert_iterator<std::list<int>> bii(list);
bii = 1;
bii = 2;
bii = 3;
bii = 4;
// list = 1, 2, 3, 4
// note: decltype(*bii) == bii&, so deferencing bii serves no purpose;
// similarly, ++bi does nothing either; both operations are just defined
// to mimick a regular operator interface so it can be used in regular
// algorithms over iterators.
}
Thus, the two approaches are equally valid, but have different behaviors:
back_insert_iterator
allows you to append to an existing containerThe semantics are different, picks whichever makes sense for the task at hand.
Upvotes: 3
Reputation: 153820
General iterators don't change the size or structure of a sequence. In particular random access iterators just access the elements at specific locations.
std::back_insert_iterator<Cont>
is a trmplate modelling a concrete output iterator changing the sequence it refers to for each element written to it: it calls cont.push_back()
for each element written. Since the iterator doesn't read the sequence it is modifying, adding elements works quite nicely.
Upvotes: 0
Reputation: 29586
They can, but it may not be safe to do so.
I suggest reading the excellent introduction to iterators from the Helsinki University.
If you have an iterator for a container (forward, bidirectional and random access are all fine), and you use it as an output iterator in an STL algorithm context, the output will be written into the container, but the iterator is never compared against the container's end()
. If all the elements written fit, then this is fine, but if not, the output iterator will reach end()
, and dereferencing it to write the next element will give undefined behaviour.
Things like back_insert_iterator
are designed specifically for use as an output iterator, and will not cause UB in this way, because they can always insert more elements.
Upvotes: 0
Reputation: 16630
Regular iterators don't know anything about the container they are being used with except for the data type its holding. In order to add elements to a container, let's say a vector, one needs to know the number of elements in the vector.
Upvotes: 0