STL Vector, Iterator and Insert (C++) - c++

I have a method to which a vector's iterator is passed.
In this method I'd like to add some elements into the vector, but I am not sure whether this is possible when having only the iterator
void GUIComponentText::AddAttributes(vector<GUIComponentAttribute*>::iterator begin, vector<GUIComponentAttribute*>::iterator end)
{
for (vector<GUIComponentAttribute*>::iterator i = begin; i != end; ++i)
{
GUIComponentAttribute &attrib = *(*i);
// Here are the GUIComponentAttribute objects analyzed - if an object of a
// special kind appears, I would like to add some elements to the vector
}
}
Thanks
Markus

In the code you show, this is not possible. Especially because you should not add/remove elements to/from a vector while you iterate over it.

This is a long standing design "issue" in the STL. Iterators do not allow the modification of the structure of the underlying sequence they are iterating over: ie you can modify (sometimes) the elements themselves, but you cannot add/remove elements. Though InputIterator and OutputIterator are a bit special in this regard... hum...
This is actually the cause of the erase/remove idiom:
vec.erase(std::remove_if(vec.begin(), vec.end(), predicate), vec.end());
So, no, sorry, there is no way to actually modify the vector.
However, as exposed above, you can perfectly use the remove_if algorithm and simply return the new end of the valid range... or you can ask for the whole vector to begin with.
As noted by Björn, modifying a sequence structure while iterating over it is error-prone.

First, you'll have to change the interface. Given two iterators,
there's no way to get back to the container to which they refer; so if
you want to modify the container, you'll have to pass a reference to it,
i.e.:
void GUIComponentText::AddAttributes(
std::vector<GUIComponentAttribute*>& attributes )
{
for ( std::vector<GUIComponentAttribute*>::iter = attributes.begin();
iter != attributes.end();
++ iter )
{
// ...
}
}
Having done that: insertion can invalidate iterators. So it depends on
where you want to insert. If you want to insert at the current
position: std::vector<>::insert of a single element returns an
iterator to that element, which was inserted before your element, so you
can assign it to your iterator, adjust (if necessary), and continue:
iter = attributes.insert(iter, newAttribute);
++ iter; // Return to where we were...
If you're appending (push_back), the problem is a bit more complex;
you need to calculate the offset, then reconstruct the iterator:
size_t offset = iter - attributes.begin();
attributes.push_back( nweAttribute );
iter = attributes.begin() + offset;
In this case, it is probably simpler to iterate using a size_t and
[], rather than an iterator.

It is not possible to add elements into a vector whilst iterating over it. In addition, you most certainly cannot add one to a vector with just a pair of iterators- you'd need a pointer/reference to the whole vector object.
The best you could do is return a vector of new components to add by the the calling function.

Related

Can we take the value of iterator which was returned from lower_bound as vector index?

I'm new to vector in C++ and trying to get how it works.
First, I have a vector array:
vector<int>container;
Then I want to get the position of a given number in a vector array.
vector<int>::iterator position;
position = lower_bound(container.begin(), container.end(), temp);
After that, I want to get the value at that position that was returned from lower_bound by
container[position]
But I get the error that
No viable overloaded operator[] for type 'vector'
When I change it into *(position+1), it works fine.
So what is the different between those two?
Welcome to stackoverflow :)
First of all, we should understand what an iterator is. According to the hackingcpp
objects that point to a location
may point to a readable memory address / object
..
There are a lot of containers in C++ STL, such as vector, list, map and others.
A iterator is an abstraction of pointer, which allows you to access elements stored in container, use algorithm(such as sort, find, lower_bound) provided by STL no matter what kind of container we have.
Therefore, the return type of std::lower_bound is an iterator as you know vector<int>::iterator,
You couldn't access an element by calling container[position], there is no such function vector[iterator] provided by vector.
When I change it into *(position+1)
*itreator means return the value of where an iterator points out.
By the way, it's dangerous to do something like this *(position+1).
Since perhaps the given value tmp is in vector, perhaps it's not, so you should check whether the given value tmp is in vector by calling iterator != vector.end().
std::lower_bound returns a ForwardIterator (See: C++ named requirements: LegacyForwardIterator).
You can dereference it just like you would a pointer, eg:
std::vector<int> container;
// ...
auto it = std::lower_bound(container.begin(), container.end(), foo);
if (container.end() == it) {
throw or_something("handle error?");
}
const int x = *it;
const int y = container[std::distance(container.begin(), it)];
In that example x == y is true. (See: Compiler Explorer)

Can I get a container object from an iterator?

std::vector<int> vec={1,2,3};
std::vector<int>::iterator it = vec.begin();
if(vec == get_vec_from_it(it)){
puts('sucesss');
}
std::vector<int> get_vec_from_it(std::vector<int>::iterator it){
/*?*/
}
How should I write get_vec_from_it function in the above example?
The basic idea is that iterators abstract away where the elements come from, there might not even be a container. Afaik there is a single type of iterator that "knows" its container and that is std::back_insert_iterator, though thats an exception. The container member is only protected so there is even a way to get the container from a std::back_insert_iterator, but thats not how it is meant to be used.
You can adance the iterator to get the next element, but you wouldn't know where to stop, because at some point you'll reach the end of the vector and there is no way to identify it. If you pass begin and end you can create a copy of the original vector:
std::vector<int> get_vec_from_it(std::vector<int>::iterator begin ,std::vector<int>::iterator end){
return {begin,end};
}
Though, thats just a different way to copy the vector and you need to know both begin and end.
I made a function that returns the iterator that points to the node but I couldn't write the stop condition in a for statement. So I wonder if the stop condition can be written like it!=get_vec_from_it(it).end()
Functions that work on a range of elements typically take a pair of iterators, first and last, to know where to stop (alternatively a first iterator and number of elements can be used). Your idea of using it!=get_vec_from_it(it).end() is overcomplicating the issue. Just pass vec.end() to the function and use that: it != end.
No.
You can create a vector from a pair of iterators, or an iterator and number of elements. Example:
std::vector<int>
get_vec_from_its(std::vector<int>::iterator first, std::vector<int>::iterator last){
return std::vector<int>(first, last);
}
// ...
if(vec == get_vec_from_it(vec.begin(), vec.end())){
The function is of course so trivial that I would recommend instead to use the constructor directly.

Erasing item in a for(-each) auto loop

Is there a way to erase specific elements when using a auto variable in a for loop like this?
for(auto a: m_Connections)
{
if(something)
{
//Erase this element
}
}
I know I can either do say
for(auto it=m_map.begin() ...
or
for(map<int,int>::iterator it=m_map.begin() ...
and manually increment the iterator (and erase) but if I could do it with less lines of code I'd be happier.
Thanks!
You can't. A range-based loop makes a simple iteration over a range simpler, but doesn't support anything that invalidates either the range, or the iterator it uses. Of course, even if that were supported, you couldn't efficiently erase an element without access to the iterator.
You'll need an old-school loop, along the lines of
for (auto it = container.begin(); it != container.end();) {
if (something) {
it = container.erase(it);
} else {
++it;
}
}
or a combination of container.erase() and std::remove_if, if you like that sort of thing.
No, there isn't. Range based for loop is used to access each element of a container once.
Every time an element is removed from the container, iterators at or after the erased element are no longer valid (and given the implementation of the range-based-for this is a problem).
You should use the normal for loop (or a while) if you need to modify the container as you go along.
If you want to erase elements for which a predicate returns true, a good way is:
m_Connections.erase(
std::remove_if(m_Connections.begin(),
m_Connections.end(),
[](Type elem) { return predicate(elem); }),
m_Connections.end());
std::remove_if doesn't mix iteration logic with the predicate.
You need the iterator if you want to erase an element from a container.
And you can't get the iterator from the element itself -- and even if you could, for instance with vector, the iterator that range-based for internally uses would be invalidated in the next step causing undefined behavior.
So the answer is: No, in its classic usage you can't. range-based for was solely designed for convenient iteration of all elements in a range.
push all elements into array and then do pop operation to remove the item

c++: popping an element by key out of an std::map

I'm interested in removing an element with a specific key out of a map and use this element.
Something that will look like:
itr = MyMap.pop(wantedKey);
//Now MyMap is missing the element which has the key 'wantedKey'.
//Do something with this element through 'itr'.
Is there an stl map method for doing this?
EDIT
Following carleeto's response, I want to clarify: What I need is the element being removed from the map and the program being able to use it afterwards, it could be the element itself as a pair, not necessarily an iterator.
There are two options: use it in-place then remove it, or move it to a local variable, remove the entry, then use it.
// use-remove
auto i = MyMap.find(wantedKey);
if (i != MyMap.end()) {
// use-remove
use(i->second);
MyMap.erase(i);
// or
// move-remove-use
auto x = std::move(i->second);
MyMap.erase(i);
use(x);
} else {
// Not found
}
Not that I know of, but you can use std::map::find to get an iterator and then call std::map::erase with said iterator as an argument when you're done.
From your variable naming, I think you might be confusing concepts here.
itr = MyMap.pop(wantedKey);
//Do something with this element through 'itr'.
Iterators only point to elements in containers. Therefore, if you had received an iterator through a function called pop (even if it existed), the iterator would reference not the element you popped, but probably the one after or before it, like std::vector::erase. This is because the purpose of an iterator is to iterate over the elements in a container. Therefore, if an element is not in the container, you cannot get an iterator to it. However, even if you used the iterator returned by the erase function, it would not reference you would be expecting it to.
So you can erase an element from the map, like so many have pointed out, by searching for it, getting the ierator to it and then calling erase with that iterator. but you cannot get an iterator that points to element you have erased. Hope this clears things up.
UPDATE: If all you want is to access the element and use it, then all you need to do use std::map::find to get an iterator and std::map::erase to remove the item from the map, once you have finished using the iterator. The reason is that even if you have stored a copy of the iterator for future use, once you call erase, it will be invalidated. To be able to access it after you have erased it, depending on scope, you will probably need to copy it.
Finally, what you want to do is a very common task - look up a map based on a key and perform an operation on the associated element. It's quite likely that you have a list of keys to go through. You should also look up functors, std::for_each and std::transform. I realise this is not operating on the element after you have removed it, but I thought I would add it in, seeing as how its a related operation. For example: You could move all elements that match a list of keys into another container (say, a vector, and then use the above to operate on them).
Probably what you want to do is
itr = MyMap.find('thing in a string');
to find the iterator and then use it,
MyMap.erase(itr)
And then erase it.
Pop() belongs to the stack datastructure. To access an element of a map, use the [] operator (http://www.cplusplus.com/reference/map/map/operator%5B%5D/), to remove it from the map use (http://www.cplusplus.com/reference/map/map/erase/).
itr = MyMap.find(wantedKey);
if(itr != MyMap.end()) {
use( itr->second );
MyMap.erase(itr);
}
your.uncle = bob;
Using C++'s std::map<T, U>::find():
map.erase(map.find(key));
The way I did it is below. In my case the map stores std::shared_ptr values, making the copy cheap(ish), and the object ownership transferral clear.
auto it = MyMap.find( wantedkey );
if ( it == MyMap.end() ) throw runtime_error("not found");
auto ret = it->second; // make copy of shared_ptr
MyMap.erase(it);
return ret;
The caller gets a shared_ptr with a reference count of at least one (from the copy). Note the function must return the shared_ptr by value, etc.

c++ iterators double loops

I'm trying to do something like the following:
myvec is a vector of Couple objects (formed each with an EntityA and an EntityB).
I'm trying to remove the duplicate couples..
Anyway sometimes the following code crashes with an out of bound it2.
The condition is fine, the iterators seems not
if(myvec.size()>1)
for(vector<Couple>::iterator it1 = myvec.begin(); it1+1 !=myvec.end();){
for(vector<Couple>::iterator it2 = it1+1; it2 !=myvec.end();){
if((it1->EntityA!=it2->EntityA&&it1->EntityA!=it2->EntityB)||
(it1->EntityB!=it2->EntityA&&it1->EntityB!=it2->EntityB)){
it2++;
}
else{
myvec.erase(it2);
}
}
it1++;
}
Any solution/alternative?
Use the existing library functions whenever possible. You will need to provide a binary predicate function, or provide an operator< and operator== for your Couples object.
std::sort(myvec.begin(), myvec.end());
myvec.erase(std::unique(myvec.begin(), myvec.end()), myvec.end());
However, it may be better for you to have a container that will automatically avoid duplicates like std::set. This is a related question, with an excellent answer.
if(myvec.size()>1)
for(vector<Couple>::iterator it1 = myvec.begin(); it1+1 !=myvec.end();){
for(vector<Couple>::iterator it2 = it1+1; it2 !=myvec.end();){
if((it1->EntityA!=it2->EntityA&&it1->EntityA!=it2->EntityB)||
(it1->EntityB!=it2->EntityA&&it1->EntityB!=it2->EntityB)){
it2++;
}
else{
it2 = myvec.erase(it2);
}
}
it1++;
}
Return value A random access iterator pointing to the new location of
the element that followed the last element erased by the function
call, which is the vector end if the operation erased the last element
in the sequence.
http://cplusplus.com/reference/stl/vector/erase/
It is not a good idea to modify the vector (to move around components and changing the vectors length in this case) you are iterating over. That is the problem with your code. When you remove one component in the middle of the vector, the ones at the end are shifted to the left, so the end of the vector moves. That messes up your break condition.
Instead of modifying your existing vector, you could create a new vector empty vector, and add to it every component you want to keep. That could consume more memory (if your vector is large, that might be of interest), but it should save you much cpu-time, because removing a component in the middle of the vector is not a cheap operation. All components right of it have to be moved, again and again, one position at a time (there exist containers in stl that don't have this problem). So building a new vector and adding to it should be better altogether.