Reseat the container an iterator "points" to - c++

Suppose I have a std::list myList and an iterator myIt that I am using to point to some element of the list.
Now I make a shallow copy copiedList of myList (so I can reorder it). Similarly I can make a copy copiedIt of myIt, however it still references myList instead of copiedList, meaning I cannot sensibly compare it against copiedList.end(), as I may have modified that list.
Is there a (standard) way to reseat copiedIt to reference copiedList instead? This should be semantically valid, as long as I have not made any changes to the copy.
My current solution is to use the original iterator to std::find the pointed-to element in the copy of the list, but while that works and causes no problems, it seems unelegant.

You can use std::next and std::distance, like this:
template <class Container>
typename Container::iterator reseat(typename Container::iterator it, const Container &source, Container &target)
{
return std::next(target.begin(), std::distance(source.begin(), it));
}
In prose: find the distance of it from the beginning of its container, and take an iterator to the same-distant element in the new container.
This could easily be generalised to allow source and target to be of different types. With slightly more work, generalisation to constant iterators is also possible.

How do you copy the list? If you'd iterate the first list and kept inserting the items individually, you'd get the iterator at each step: http://www.cplusplus.com/reference/list/list/insert/
This is because list::insert returns an iterator that points to the first of the newly inserted elements.

Related

Can I initialize an iterator with null, and if not why?

I'm trying to initialize an iterator to NULL, but it's not working. Can anyone help me?
If a pointer can be initialized with null, why can't we do that for an iterator?
vector<int> bob;
vector<int>::iterator it=NULL;
I want to initialize an iterator in a class constructor so that at the time of creation of an object of the class, the iterator should be set to NULL (default).
No, in general you cannot initialize an iterator with NULL. The iterator requirements do not require an iterator to be assignable or initializable from either an integer type or std::nullptr_t, the possible types that NULL can have.
There is no point in trying to do that. It is simply not needed. But since you have not explained why you would try to do that, I can't really make any further suggestions.
Regarding your further questions in the comments: You can value-initialize every forward iterator:
vector<int>::iterator it{}; // value-initialized
Since C++14 you are guaranteed that comparing iterators of the same type constructed in this way compare equal.
All container iterators are forward iterators.
The best equivalent to NULL or null_ptr for an iterator is the container::end() value. It likewise does not point to a valid element of the container. So, instead of testing the predicate (i == null_ptr), test (i == v.end()), and initialize as auto i = v.end();.
You can't initialize the iterator until after you have a container. But that is not a problem in practice because an iterator makes no sense without a container it refers to.
If you have complicated code that uses iterators, and you want to isolate that code in a function, you will have to pass two iterators to the function: the current or beginning iterator, and the end iterator. This is what the STL does.
An iterator is initialized when created.
You can null check its _Ptr member:
vector<int>::iterator p;
if (p._Ptr == NULL) ...
(using clang & dinkumware)

Iterator to last element of std::vector using end()--

I have a std::vector and I want the iterator to the last element in the vector; I will be storing this iterator for later use.
NOTE: I want an iterator reference to it, not std::vector::back. Because I want to be able to compute the index of this object from the std::vector::begin later on.
The following is my logic to get the iterator to the last element:
std::vector<int> container;
std::vector<int>::iterator it = container.end()--;
Since std::vector::end has O(1) time complexity, is there a better way to do this?
I think you mean either:
std::vector<int>::iterator it = --container.end();
std::vector<int>::iterator it = container.end() - 1;
std::vector<int>::iterator it = std::prev(container.end());
You're unintentionally just returning end(). But the problem with all of these is what happens when the vector is empty, otherwise they're all do the right thing in constant time. Though if the vector is empty, there's no last element anyway.
Also be careful when storing iterators - they can get invalidated.
Note that if vector<T>::iterator is just T* (which would be valid), the first form above is ill-formed. The second two work regardless, so are preferable.
You have rbegin that does what you need
cplusplus reference
auto last = container.rbegin();
The way you are doing it will give you the wrong iterator because post increment will not change the value until after the assignment.
There is always this:
auto it = std::prev(container.end());
Remember to check first that the container is not empty so your iterator exists in a valid range.

implementing an iterator of certain class

when creating an iterator of a vector, the iterator itself is a pointer to the values held by the vector. therefore *iterator is actually the value held by the vector.
so I have two questions:
when using an iterator on a map, what is the iterator actually? I mean, what is it's inner implementation? is it like a struct that holds different data members?
If I want to implement my own iterator, which holds several data members, what am I actually returning when creating an iterator?
Depends on implementation. Usually, std::map is implemented as a balanced binary search tree. In that case, the iterator would likely point to a node in the tree.
The iterator of a std::map is a structure that references the key-value-pairs saved in your map. The standard iterator which you get with i.e begin() or end() is a bidirectional iterator. This means that your can call ++i and --i operators on the iterator object to move for/backward between the items saved in your map.
Why do you want to implement your own iterator? Maybe creating a class or struct saving it to a std::vector<T> will do what you want?! you can access the iterator by std::vector<T>::iterator. If you really want to implement your own iterator, you should ask yourself the question if it should work for your own data structures as a test, or if you want to be compatible with i.e. std data structures. If its the latter, you should derive from a iterator implementation and modify it the way of your needs. Look at this answer as well.
The iterator itself is a pointer to the values held by the vector.
Vector iterator is not a pointer to value but a class with operator * implemented, which returns the value held by the container and pointed out by your iterator. In case of map you can access key and value using first and second fields:
map<string, int> wheelMap;
wheelMap["Car"] = 4;
map<string, int>::iterator itWheel = wheelMap.begin();
cout << itWheel ->first << ":" << itWheel ->second << endl; //This will print: Car:4
Map iterator has also other operators implemented: +, ++, -, --, ->, ==, !=.
Additionally vector has also the operator [] implemented to get the values by index.
I implemented std::map-like container as red-black-tree (like often sited being used for std::map implementation) and only thing the iterator implementation needs (both const and non-const versions) is pointer to the tree node. Each tree node contains pointers to the two children and parent (plus color bit) which is enough to traverse the tree to either direction. In general it depends on the container & iterator types (and implementation) though what kind of data is needed to implement its functionality. E.g. my deque iterators have pointer to the container and index of the element, but it's really implementation specific how the iterators are implemented and what data they need.

Appending to a container

I have a doubt regarding the generic algorithm copy in C++.
To copy from destination container ret and source container bottom,
copy(bottom.begin(), bottom.end(), back_inserter(ret));
works but
copy(bottom.begin(), bottom.end(), ret.end());
does not. Do these two statements have different implications?
Check what the statements do – there is no magic involved. In particular, copy is (essentially) just a loop. Simplified:
template <typename I>
void copy(I begin, I end, I target) {
while (begin != end)
*target++ = *begin++;
}
And back_inserter really does what the name says.
So in effect, without theback_inserter you do not expand the target container, you just write past its end: iterators don’t change their underlying container. The back_inserter function, on the other hand, creates a specialised iterator which does hold a reference to its original container and calls push_back when you dereference and assign to it.
In the first one you are giving copy a method of inserting, and from what container to insert from.
In the second one you are only giving a pointer to the end of the container.
Both return iterators, but...
ret.end() returns an iterator pointing to the end of the
container. It can be decremented, but not incremented (since it
already points to the end of the sequence), and it cannot be
dereferenced unless it is decremented (again, because it points
to one past the end of the sequence).
back_inserter(ret) is a function which returns
a back_insertion_iterator, which is a very special type of
"iterator" (category OutputIterator): it's incrementation
functions are no-ops, dereferencing it returns *this, and
assigning a value type to it calls push_back on the owning
container. (In other words, it's not an iterator at all, except
for the C++ standard; but it presents the interface of one to do
something very different.)

vector::erase and reverse_iterator

I have a collection of elements in a std::vector that are sorted in a descending order starting from the first element. I have to use a vector because I need to have the elements in a contiguous chunk of memory. And I have a collection holding many instances of vectors with the described characteristics (always sorted in a descending order).
Now, sometimes, when I find out that I have too many elements in the greater collection (the one that holds these vectors), I discard the smallest elements from these vectors some way similar to this pseudo-code:
grand_collection: collection that holds these vectors
T: type argument of my vector
C: the type that is a member of T, that participates in the < comparison (this is what sorts data before they hit any of the vectors).
std::map<C, std::pair<T::const_reverse_iterator, std::vector<T>&>> what_to_delete;
iterate(it = grand_collection.begin() -> grand_collection.end())
{
iterate(vect_rit = it->rbegin() -> it->rend())
{
// ...
what_to_delete <- (vect_rit->C, pair(vect_rit, *it))
if (what_to_delete.size() > threshold)
what_to_delete.erase(what_to_delete.begin());
// ...
}
}
Now, after running this code, in what_to_delete I have a collection of iterators pointing to the original vectors that I want to remove from these vectors (overall smallest values). Remember, the original vectors are sorted before they hit this code, which means that for any what_to_delete[0 - n] there is no way that an iterator on position n - m would point to an element further from the beginning of the same vector than n, where m > 0.
When erasing elements from the original vectors, I have to convert a reverse_iterator to iterator. To do this, I rely on C++11's §24.4.1/1:
The relationship between reverse_iterator and iterator is
&*(reverse_iterator(i)) == &*(i- 1)
Which means that to delete a vect_rit, I use:
vector.erase(--vect_rit.base());
Now, according to C++11 standard §23.3.6.5/3:
iterator erase(const_iterator position); Effects: Invalidates
iterators and references at or after the point of the erase.
How does this work with reverse_iterators? Are reverse_iterators internally implemented with a reference to a vector's real beginning (vector[0]) and transforming that vect_rit to a classic iterator so then erasing would be safe? Or does reverse_iterator use rbegin() (which is vector[vector.size()]) as a reference point and deleting anything that is further from vector's 0-index would still invalidate my reverse iterator?
Edit:
Looks like reverse_iterator uses rbegin() as its reference point. Erasing elements the way I described was giving me errors about a non-deferenceable iterator after the first element was deleted. Whereas when storing classic iterators (converting to const_iterator) while inserting to what_to_delete worked correctly.
Now, for future reference, does The Standard specify what should be treated as a reference point in case of a random-access reverse_iterator? Or this is an implementation detail?
Thanks!
In the question you have already quoted exactly what the standard says a reverse_iterator is:
The relationship between reverse_iterator and iterator is &*(reverse_iterator(i)) == &*(i- 1)
Remember that a reverse_iterator is just an 'adaptor' on top of the underlying iterator (reverse_iterator::current). The 'reference point', as you put it, for a reverse_iterator is that wrapped iterator, current. All operations on the reverse_iterator really occur on that underlying iterator. You can obtain that iterator using the reverse_iterator::base() function.
If you erase --vect_rit.base(), you are in effect erasing --current, so current will be invalidated.
As a side note, the expression --vect_rit.base() might not always compile. If the iterator is actually just a raw pointer (as might be the case for a vector), then vect_rit.base() returns an rvalue (a prvalue in C++11 terms), so the pre-decrement operator won't work on it since that operator needs a modifiable lvalue. See "Item 28: Understand how to use a reverse_iterator's base iterator" in "Effective STL" by Scott Meyers. (an early version of the item can be found online in "Guideline 3" of http://www.drdobbs.com/three-guidelines-for-effective-iterator/184401406).
You can use the even uglier expression, (++vect_rit).base(), to avoid that problem. Or since you're dealing with a vector and random access iterators: vect_rit.base() - 1
Either way, vect_rit is invalidated by the erase because vect_rit.current is invalidated.
However, remember that vector::erase() returns a valid iterator to the new location of the element that followed the one that was just erased. You can use that to 're-synchronize' vect_rit:
vect_rit = vector_type::reverse_iterator( vector.erase(vect_rit.base() - 1));
From a standardese point of view (and I'll admit, I'm not an expert on the standard): From §24.5.1.1:
namespace std {
template <class Iterator>
class reverse_iterator ...
{
...
Iterator base() const; // explicit
...
protected:
Iterator current;
...
};
}
And from §24.5.1.3.3:
Iterator base() const; // explicit
Returns: current.
Thus it seems to me that so long as you don't erase anything in the vector before what one of your reverse_iterators points to, said reverse_iterator should remain valid.
Of course, given your description, there is one catch: if you have two contiguous elements in your vector that you end up wanting to delete, the fact that you vector.erase(--vector_rit.base()) means that you've invalidated the reverse_iterator "pointing" to the immediately preceeding element, and so your next vector.erase(...) is undefined behavior.
Just in case that's clear as mud, let me say that differently:
std::vector<T> v=...;
...
// it_1 and it_2 are contiguous
std::vector<T>::reverse_iterator it_1=v.rend();
std::vector<T>::reverse_iterator it_2=it_1;
--it_2;
// Erase everything after it_1's pointee:
// convert from reverse_iterator to iterator
std::vector<T>::iterator tmp_it=it_1.base();
// but that points one too far in, so decrement;
--tmp_it;
// of course, now tmp_it points at it_2's base:
assert(tmp_it == it_2.base());
// perform erasure
v.erase(tmp_it); // invalidates all iterators pointing at or past *tmp_it
// (like, say it_2.base()...)
// now delete it_2's pointee:
std::vector<T>::iterator tmp_it_2=it_2.base(); // note, invalid iterator!
// undefined behavior:
--tmp_it_2;
v.erase(tmp_it_2);
In practice, I suspect that you'll run into two possible implementations: more commonly, the underlying iterator will be little more than a (suitably wrapped) raw pointer, and so everything will work perfectly happily. Less commonly, the iterator might actually try to track invalidations/perform bounds checking (didn't Dinkumware STL do such things when compiled in debug mode at one point?), and just might yell at you.
The reverse_iterator, just like the normal iterator, points to a certain position in the vector. Implementation details are irrelevant, but if you must know, they both are (in a typical implementation) just plain old pointers inside. The difference is the direction. The reverse iterator has its + and - reversed w.r.t. the regular iterator (and also ++ and --, > and < etc).
This is interesting to know, but doesn't really imply an answer to the main question.
If you read the language carefully, it says:
Invalidates iterators and references at or after the point of the erase.
References do not have a built-in sense of direction. Hence, the language clearly refers to the container's own sense of direction. Positions after the point of the erase are those with higher indices. Hence, the iterator's direction is irrelevant here.