Domanda

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);
}
È stato utile?

Soluzione 2

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.

Altri suggerimenti

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 lists, 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.

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top