Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Does passing containers by value invalidate iterators?

Here is some example code:

#include <iostream>
#include <vector>

template <typename T>
std::vector<typename T::iterator> f(T t)
{
        std::vector<typename T::iterator> v;
        for (auto i = t.begin(); i != t.end(); ++i)
        {
                v.push_back(i);
        }
        return v;
}

template <typename T>
void print(const std::vector<T>& v)
{
        for (auto i = v.begin(); i != v.end(); ++i)
        {
                std::cout << **i << ' ';
        }
        std::cout << std::endl;
}

int main()
{
        std::vector<int> v{1, 2, 3};
        print(f(v));
        std::vector<std::vector<int>::iterator> itervec = f(v);
        print(itervec);
}

On ideone the output was:

1 2 3 
163487776 2 3 

Questions

If I change f(T t) to f(T& t) the output is as expected. I'm assuming because I am working with copies of containers, technically the iterators I am pushing back on the vector are not the same as the vector I created in main. Is this correct? The one thing I noticed is print(f(v)); prints 1 2 3 as expected but as soon as I assign it to itervec the first iterator becomes garbage, is this all implementation dependent?

like image 866
Jesse Good Avatar asked Apr 11 '12 20:04

Jesse Good


People also ask

What invalidates an iterator?

An Iterator becomes invalidate when the container it points to changes its shape internally i.e. move elements from one location to another and the initial iterator still points to old invalid location. Iterator invalidation in vector happens when, An element is inserted to vector at any location.

What causes iterator invalidation?

Iterator Invalidation in C++ When the container to which an Iterator points changes shape internally, i.e. when elements are moved from one position to another, and the initial iterator still points to the old invalid location, then it is called Iterator invalidation. One should be careful while using iterators in C++.

How can we avoid iterator invalidation?

To avoid invalidation of references to elements you can use a std::deque if you do not insert or erase in the middle. To avoid invalidation of iterators you can use a std::list.

Does std :: sort invalidate iterators?

std::sort will not invalidate iterators to a vector. The sort template uses the * operator on the iterators to access and modify the contents of the vector, and modifying a vector element though an iterator to an element already in the vector will not invalidate any iterators.


2 Answers

Yes, the iterators are iterators only valid for the local object v in the function f, and at the end of f, v goes out of scope and is destroyed, and the iterators are invalid.

You have to pass the vector by reference (or pointer or whatever) so that the iterators you store are the iterators for the original object that the caller passes in, not for a temporary copy stored in a local variable.

The behaviour you are seeing is undefined, so it just happens to print the first three and last two correctly.

like image 161
Seth Carnegie Avatar answered Sep 28 '22 04:09

Seth Carnegie


Yes, because you are recieving a temporary and returning an iterator of that temporary. After function exit the temporary gets cleaned up, invalidating the iterator.

If you however get passed a reference both main and print are accessing the same object. Since this object persists after function exit the iterator is not invalidated.

like image 22
orlp Avatar answered Sep 28 '22 04:09

orlp