Assigning (operator =) overrides iterators for containers

I have some code similar to this:

std::vector<int> v1 = { 1, 2, 3, 4 };  
std::vector<int> v2 = { 7, 8, 9, 10 };  
std::vector<int>::iterator it = std::next(v1.begin());  
v1 = v2;  
int test = *it;  
std::cout << test;   

      

The above code throws an error: the iterator cannot be dereferenced.

However, if I replace the vector with a list like this:

std::list<int> v1 = { 1, 2, 3, 4 };  
std::list<int> v2 = { 7, 8, 9, 10 };  
std::list<int>::iterator it = std::next(v1.begin());  
v1 = v2;  
int test = *it;  
std::cout << test;   

      

The code just ran as expected with no errors.
From the invalidation rules of Iterator and std :: list :: operator = , I was told that after calling the operator =, all iterators, references, and pointers associated with that container are invalidated except for trailing iterators. But why does this code work with std :: list? Am I missing something important?

+1


source to share


2 answers


If the iterator is invalid, dereferencing it is undefined. Therefore, whatever behavior you observe while doing this is irrelevant as far as the specification is concerned. "Work", as you expect, refers to acceptable behavior.

FWIW (not much, tbh), I would expect the assignment operator to std::list

be implemented equivalently something like this:



list& operator=(list const& rhs) {
    if (this == &rhs)
        return *this;

    auto lhs_i = begin();
    auto rhs_i = rhs.begin();

    // write over the elements in any currently existing nodes, this
    // avoids any unnecessary allocations
    while (lhs_i != end() && rhs_i != rhs.end()) {
        *lhs_i++ = *rhs_i++;
    }

    // erase any extra elements if size() > rhs.size()
    erase(lhs_i, end());

    // push back additional elements if size() < rhs.size()
    while (rhs_i != rhs.end()) {
        push_back(*rhs_i++);
    }

    return *this;
}

      

And if so, you can see that for a case like yours, where the lists have the same number of elements, no element is created or destroyed, and so you would very much expect the iterators to continue working as usual. This is, of course, completely speculation and definitely not behavior that you should rely on, because even if it is the case for your implementation, they may change it in the next version without notice.

+4


source


This behavior is undefined, but GCC has debug containers to catch this behavior.

Enable it with -D_GLIBCXX_DEBUG

:



     int test = *it;
         ^~~~
/usr/local/include/c++/6.1.0/debug/safe_iterator.h:270:

Error: attempt to dereference a singular iterator.

Objects involved in the operation:

    iterator "this" @ 0x0x7fff5f561e90 {
      type = __gnu_debug::_Safe_iterator<std::__cxx1998::_List_iterator<int>, std::__debug::list<int, std::allocator<int> > > (mutable iterator);
      state = singular;
      references sequence with type 'std::__debug::list<int, std::allocator<int> >' @ 0x0x7fff5f561ef0
    }
bash: line 7: 16071 Aborted                 (core dumped) ./a.out

      

+1


source







All Articles