I do not understand why this code is accurate
vector<int> coll;
coll.reserve(2*coll.size());
copy (
coll.begin(), coll.end(), // zrodlo
back_inserter(coll) // przeznaczenie
);
coll.end()
represents the end of vector. After I push_back anything (as back_insert_iterator
does) what coll.end()
returns is the same what was before or something different? Are there more than one terminating iterator? Why end() can be use as an end of container even when new content is added?
Moreover, you cannot apply the code to list container - it gets stuck. That is important because in case of vector push_back makes iterators unreliable after reallocating data (when size()==capacity()
and push_back()
called) whereas in case of list that's not the case. Than why the code hangs for list?
Edit: (sscce)
#include <iostream>
#include <list>
#include <algorithm>
using namespace std;
template <class T>
inline void PRINT_ELEMENTS (const T& coll, const char* optcstr="")
{
typename T::const_iterator pos;
std::cout << optcstr;
for (pos=coll.begin(); pos!=coll.end(); ++pos) {
std::cout << *pos << ' ';
}
std::cout << std::endl;
}
int main(){
list<int> coll;
list<int>::iterator end = coll.end();
copy (
coll.begin(), coll.end(), // zrodlo
back_inserter(coll) // przeznaczenie
);
cout << *end << endl;
PRINT_ELEMENTS(coll);
}
C++ Algorithm Function copy() C++ Algorithm copy() function is used to copy all the elements of the container [first,last] into a different container starting from result.
back_inserter() returns a back_insert_iterator , which has to function like an output iterator. Specifically, it has to support operations such as pre- and post increment, and dereference assignment. If it didn't support those operations, it couldn't be used where output iterators are required.
A back-insert iterator is a special type of output iterator designed to allow algorithms that usually overwrite elements (such as copy) to instead insert new elements automatically at the end of the container. Syntax: std::back_inserter (Container& x); x: Container in which new elements will be inserted at the end.
The back_inserter provides a method in C++ Programming Language that constructs an iterator and does the operation of inserting new elements to a list through the end. Using back_inserter has the advantage that we don't need to have a proper number of the list.
coll.end()
is called before the copying and back insertion begins, so essentially the code is the same as
coll.reserve(2*coll.size());
auto oldEnd = coll.end();
copy (coll.begin(), oldEnd, back_inserter(coll) );
meaning, copy
will not re-evaluate coll.end()
, so it will not notice/bother that it is inserting into the same vector, and that what once was the end of the vector is not the end after the first insertions.
The same algorithm will not compile for lists, because std::list
has no reserve
method. However, without reserve
it should work for list
s, since std::list::push_back
does not invalidate iterators.
You are right that std::vector::push_back
invalidates iterators when reallocation occurs, so it's very important to do the reserve
call, because it makes sure no reallocation is needed.
the begin()
and end()
pointers/iterators used to determine what is to be copied are evaluated once when the function is called. So essentially std::copy()
will increment it's cursor iterator which will eventually reach end()
, because vector<T>::const_iterator
is just a fancy T*
pointer on an old school array.
As you correctly mentionned, if a push_back
makes the vector
reallocate and move the data somewhere else in memory, then the next element copied will have a wrong address for source, which is likely to end up with a segmentaion fault.
For a list, it can never terminate because end()
is a sentinel/guard pointer, and you can only reach end()
by incrementing the iterator on the last element of the list. So the address of end()
itself never changes, but because you are constantly adding an element at the end of the list, you will never reach the last element, so std::copy()
can never obtain a pointer to end()
. Kind of like a dog chasing it's tail.
It's easier to understand with illustrations and diagrams, read-up on doubly linked list and sentinel nodes, it will all make sense.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With