Passing an iterator and vector to a function - c++

I have some function
void print_elem(const std::vector<int>::iterator it, const std::vector<int> &vec) {/*.....*/}
If I leave out that the vector is a reference to the original object, I get copies of the vector. Why doesn't the same hold true for the iterator? Why doesn't the iterator need to be a reference also?
For instance if I wanted to iterate through the vector, print each element and wanted to stop when I hit the end of the vector, unless I pass a reference to the vector the iteration just continuously iterates through the first vector copy. But if I pass through a reference the iteration goes through the original vector object. But why does the iterator not get copied the way the vector without reference does?

The iterator models a pointer, and it most likely either is one, or contains one which points to the vector or its contents. When you copy it, the copy is in fact a different iterator, but it stores the same value, so it still points to the same thing, just like a pointer would.

Related

Reseat the container an iterator "points" to

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.

How to dynamically update the condition of a for loop in C++?

Take the following code snippet:
// x is a global vector object that holds values of type string as follows, vector<string> x
// x is filled/populated via the function Populate_x(y,z);
Populate_x(y,z);
for (auto i : x)
{
string v = check(i);
Populate_x(v,v);
}
My question is, how can I dynamically update x in the range based for loop shown above when calling Populate_x(v,v) from within the for loop? I'm not sure if this is even possible. If not, how can I restructure my code to achieve this behavior?
Your suggestions are greatly appreciated.
A range-based for loop is equivalent to, approximately: 1) get the container's beginning and ending iterator, 2) Use a temporary iterator to iterate from the beginning iterator value to the ending iterator value 3) On each iteration, dereference the temporary iterator and set the range loop's variable to the dereferenced iterator value.
... more or less. The bottom line is 1) a range-based for loop obtains and uses the beginning and the ending iterators for the range, and 2) you state that your container is a vector, and, as you know, modifying a vector is going to invalidate most iterators to the contents of the vector.
It is true that with certain kinds of modifications to the contents of the vector, certain iterators will not be invalidated, and will remain valid. But, practically, it is safe to assume that if you've got a std::vector::iterator or a std::vector::const_iterator somewhere, modifying a vector means that the iterator is no longer valid. Not always 100% true, as I mentioned, but that's a pretty safe assumption to make.
And since range iteration obtains and uses iterators to the container, for the lifetime of the iteration, that makes, pretty much, doing a range iteration over a vector, and modifying the vector during iteration, a non-starter. Any modifications to the vector will likely result in undefined behavior, for any continuing iteration over the vector.
Note that "modification" means, essentially, insertion or removal of values from the vector; that is, modification of the vector itself. Modifying the values in the vector has no effect on the validity of any existing iterators, and this is safe.
If you want to iterate over a vector, and then safely modify the vector during this process (with the modification consisting of inserting or removing value from the vector), the first question you have to answer yourself is what does the insertion or removal mean for your iteration. That's something that you have to figure out yourself. If, for example, your loop is currently on the 4th element in the vector, and you insert a new 2nd value in the vector, since inserting a value into the vector shifts all the remaining values in the vector up, the 4th element in the vector will become the 5th element in the vector, and if you manage to safely do this correctly, on the next iteration, the 5th element in the vector will be the same one you just iterated over previously. Is this what you want? That's something you will have to answer yourself, first.
But as far as safely modifying a vector during iteration, the most simplest way is to avoid using iterators entirely, and use an index variable:
for (size_t i=0; i<x.size(); ++i)
{
auto v=x[i];
// ...
}
Now, modifying the vector is perfectly safe, and all you have to figure out, then, is what has to happen after the vector gets modified, whether the i index variable needs adjusting, in any way.
And that question you'll have to answer yourself.
From your description, I'm not sure what you mean by dynamically update x.
Is Populate_x adding new elements to x ?
Assuming your Populate_x function tries to push_back some elements on your vector x, then you cannot do that. See this answer for more details
Modifying the vector inside the loop results in undefined behaviour because the iterators used by the loop are invalidated when the vector is modified.
If so, if you want to add multiple elements at the end of x, a practical way would be to use a temporary vector<string> y; , push_back/emplace_back elements into y, and then once you're ready to append all elements of y into x , do something similar to this (in this case v1 is your x, v2 is your y :
x.insert(x.end(), make_move_iterator(y.begin()), make_move_iterator(y.end()));

Converting between pointers and references

std::list<Value> stdList;
stdList.push_back(Value());
Value * ptr = &stdList.back(); // <-- what will this address point to?
If I take the reference returned by back() and implicitly convert it to the less generic Value *, will it point to the last value of the list, or will it point to someplace unexpected?
And is there a way to create an iterator from a pointer, for use with std::list functions such as erase()? I realize generic to specific (iterator to pointer) is far more feasible than going the other direction, but I thought I'd ask anyway.
The pointer will point to the value as it is stored inside the container. The reference did the same thing.
You can't turn that pointer into a list iterator directly, because you've lost all the information about the surrounding structure. To do this you would have to get clever with list::find.
What you are trying to do it is sometimes done using vector. The reason you can turn a vector data element pointer into an iterator (array index) is because you know the structure of a vector.
Please note that list::back() does not return an iterator. It returns a reference. The two are quite different. Are you thinking about list::end()? Or are you genuinely confused between iterators and references? Because you can get a reference from a pointer. You do it like this:
Value& refval = *ptr;
Yes, The pointer point to the last value stored in the list.
We can not create an iterator from a pointer, iterator is a concept for the container (list here), and iterator don't care about what kind of value stored on the list.
Reference and pointer are handle for the value stored on the list, they are interchangeable, we can convert a reference to a pointer and vice versa.

how i can erase an element form list using pointer to this element in c++

typedef struct value
{
char* contents;
int size;
}Value;
hash_map<Key,list<Value>,hash<Key>,eqKey> dspace;
hash_map<Key, list<Value>, hash<Key>, eqKey>::iterator itr;
list<Value> vallist;
list<Value>::iterator valitr;
Value * ptr;
itr=dspace.find(searchKey);
valitr=(itr->second).begin();
valitr++;
ptr=&*valitr;
here ptr pointer is pointing to the address of the element pointed by the valitr iterator. Now I want to erase this element from the list using this pointer. I have found that list.erase function do this but I have to provide the position or iterator .
Please give me some idea how I can erase this element using pointer instead of going through the list .
valitr denotes the position in the list. *valitr dereferences the iterator, giving you a reference to the value data of that pointer, which no longer has any reference to the list it is stored in.
If you need indeed erase a certain element in the list, and not just go for the 2nd element, you have to scan the list (from begin() to end(), and check the condition for finding the element, and use erase using the iterator to that element.
The API of the list does not intend to have elements deleted by pointer. You need the iterator.
Depending on the implementation you are using, there might be ways to get the element's interator from a pointer, but that is not guaranteed. And it might change later.
Try to keep the iterator somehow.

why can't I dereference an iterator?

If I have a vector of objects (plain objects, not pointers or references), why can't I do this?
Object* object;
object = vectorOfObjects.end();
or
object = &vectorOfObjects.end();
or
object = &(*vectorOfObjects.end());
also the same question if 'object' was a reference.
They are three separate errors:
object = vectorOfObjects.end();
won't work because end() returns a an iterator, and object is a pointer. Those are generally distinct types (A vector can use raw pointers as iterators, but not all implementations do it. Other containers require special iterator types).
object = &vectorOfObjects.end();
doesn't work because you take the address of the returned iterator. That is, you get a pointer to an iterator, rather than a pointer to an Object.
object = &(*vectorOfObjects.end());
doesn't work because the end iterator doesn't point to a valid element. It points one past the end of the sequence. And so, it can't be dereferenced. You can dereference the last element in the sequence (which would be --vectorOfObjects.end()), but not an iterator pointing past the end.
Finally, the underlying problem/confusion might be that you think an iterator can be converted to a pointer. In general, it can't. If your container is a vector, you're guaranteed that elements are allocated contiguously, like in an array, and so a pointer will work. But for, say, a list, a pointer to an element is useless. It doesn't give you any way to reach the next element.
Because .end() returns an iterator, not an Object, which isn't even a valid member of the vector.
Even using begin(), which returns a valid object, you'd need the following:
The correct way would be:
std::vector<Object> vec;
std::vector<Object>::iterator it;
//....
it = vec.begin();
Object o = *it;
This is because by convention end does not point to a valid location in the container: it sits at a location one past the end of the container, so dereferencing it is illegal.
The situation with begin() is different: you can dereference it when v.begin() != v.end() (i.e. when the container is not empty):
object = *vectorOfObjects.begin();
If you need to access the last element, you can do this:
vector<object>::const_iterator i = vectorOfObjects.end();
i--;
cout << *i << endl; // Prints the last element of the container
Again, this works only when your vector contains at least one element.
Object* means a pointer to an Object, where a pointer is a memory address. But the .end() method returns an Iterator, which is an object itself, not a memory address.