Say I have something like this
vector<foo*> f;
Now suppose I have a method like this
void RemoveFromFoo(foo* fptr)
{
//search vector and remove if present
}
will something like this work ?
f.erase(std::remove(f.begin(), f.end(), fptr ), f.end());
Yes. That's the erase-remove idiom for removing selected elements from a contatiner.
remove will move all the elements you want to keep (those which don't equal fptr) to the start of the sequence, and return an iterator to the first element after them (the first element you want to erase).
Then erase will erase the elements from there until the end from the container, leaving just the ones at the start which you want to keep.
Related
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.
I have a function that iterates through a vector and calls another function to execute its contents in some manner. As a result of that execution new elements could be added to the vector. Function code is as follows:
void foo() {
for (std::vector<Item*>::iterator it = item_list.begin(); it != item_list.end(); ++it ) {
if (/*some condition*/) {
bar(it);
}
}
}
While I was googling this problem I saw that iterator might get invalidated if resize happens, but the writer was not specific on why nor when or what is the proper way of handling this problem.
As a vector is random access, you can store the distance temporarily and re-create the iterator afterwards:
void foo() {
for (std::vector<Item*>::iterator it = item_list.begin(); it != item_list.end(); ++it ) {
if (/*some condition*/) {
const auto d = std::distance( item_list.begin(), it );
bar(it);
it = item_list.begin();
std::advance( it, d );
}
}
}
The answer assumes that new elements are added after the current position, e.g., at the end. It also assumes that it is desirable that the new elements are also part of the iteration, i.e., they will also be checked against some condition and bar will be called if they do match.
Instead of inserting more items on that vector within a interaction, simply create another vector to store all the items and do the resizing while you iterate in this one, then you discard the current one and swap for that updated vector.
That's the standard way to do that.
According to the documentation, if inserting an element changes the capacity of the vector, all iterators are invalidated.
To work around this, add new items to a temporary vector and merge the two after you're done.
Alright. For the sake of other (more simple but not explanatory enough) questions that this might look like, I am not asking if this is possible or impossible (because I found that out already), I am asking if there is a lighter alternative to my question.
What I have is what would be considered a main class, and in that main class, there is a variable that references to a 'World Map' class. In essence, this 'WorldMap' class is a container of other class variables. The main class does all of the looping and updates all of the respective objects that are active. There are times in this loop that I need to delete an object of a vector that is deep inside a recursive set of containers (As shown in the code provided). It would be extremely tedious to repeatedly have to reference the necessary variable as a pointer to another pointer (and so on) to point to the specific object I need, and later erase it (this was the concept I used before switching to C++11) so instead I have a range for loop (also shown in the code). My example code shows the idea that I have in place, where I want to cut down on the tedium as well as make the code a lot more readable.
This is the example code:
struct item{
int stat;
};
struct character{
int otherStat;
std::vector<item> myItems;
};
struct charContainer{
std::map<int, character> myChars;
};
int main(){
//...
charContainer box;
//I want to do something closer to this
for(item targItem: box.myChars[iter].myItems){
//Then I only have to use targItem as the reference
if(targItem.isFinished)
box.myChars[iter].myItems.erase(targItem);
}
//Instead of doing this
for(int a=0;a<box.myChars[iter].myItems.size();a++){
//Then I have to repeatedly use box.myChars[iter].myItems[a]
if(box.myChars[iter].myItems[a].isFinished)
box.myChars[iter].myItems.erase(box.myChars[iter].myItems[a]);
}
}
TLDR: I want to remove the tedium of repeatedly calling the full reference by using the new range for loops shown in C++11.
EDIT: I am not trying to delete the elements all at once. I am asking how I would delete them in the matter of the first loop. I am deleting them when I am done with them externally (via an if statement). How would I delete specific elements, NOT all of them?
If you simply want to clear an std::vector, there is a very simple method you can use:
std::vector<item> v;
// Fill v with elements...
v.clear(); // Removes all elements from v.
In addition to this, I'd like to point out that [1] to erase an element in a vector requires the usage of iterators, and [2] even if your approach was allowed, erasing elements from a vector inside a for loop is a bad idea if you are not careful. Suppose your vector has 5 elements:
std::vector<int> v = { 1, 2, 3, 4, 5 };
Then your loop would have the following effect:
First iteration: a == 0, size() == 5. We remove the first element, then the vector will contain {2, 3, 4, 5}
Second iteration: a == 1, size() == 4. We then remove the second element, then the vector will contain {2,4,5}
Third iteration: a == 2, size() == 3. We remove the third element, and we are left with the final result {2,4}.
Since this does not actually empty the vector, I suppose it is not what you were looking for.
If instead you have some particular condition that you want to apply to remove the elements, it is very easily applied in C++11 in the following way:
std::vector<MyType> v = { /* initialize vector */ };
// The following is a lambda, which is a function you can store in a variable.
// Here we use it to represent the condition that should be used to remove
// elements from the vector v.
auto isToRemove = [](const MyType & value){
return /* true if to remove, false if not */
};
// A vector can remove multiple elements at the same time using its method erase().
// Erase will remove all elements within a specified range. We use this method
// together with another method provided by the standard library: remove_if.
// What it does is it deletes all elements for which a particular predicate
// returns true within a range, and leaves the empty spaces at the end.
v.erase( std::remove_if( std::begin(v), std::end(v), isToRemove ), std::end(v) );
// Done!
I am deleting them when I am done with them externally (via an if statement). How would I delete specific elements, NOT all of them?
In my opinion, you're looking at this the wrong way. Writing loops to delete items from a sequence container is always problematic and not recommended. Strive to stay away from removing items in this fashion.
When you work with containers, you should strategically set up your code so that you place the deleted or "about to be deleted" items in a part of the container that is easily accessed, away from the items in the container that you do not want to delete. At the time you actually do want to remove them, you know where they are and thus can call some function to expel them from the container.
One answer was already given, and that is to use the erase-remove(if) idiom. When you call remove or remove_if, the items that are "bad" are moved to the end of the container. The return value for remove(_if) is the iterator to the start of the items that will be removed. Then you feed this iterator to the vector::erase method to delete these items permanently from the container.
The other solution (but probably less used) is the std::partition algorithm. The std::partition also can move the "bad" items to the end of the container, but unlike remove(_if), the items are still valid (i.e. you can leave them at the end of the container and still use them safely). Then later on, you can remove them as you wish in a separate step since std::partition also returns an iterator.
Why not have a standard iterator iterating over a vector. That way you can delete the element by passing an iterator. Then .erase() will return the next available iterator. And if your next iterator is iterator::end() then your loop will exit.
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.
I have a std::set and I need to erase similar adjacent elements:
DnaSet::const_iterator next = dna_list.begin();
DnaSet::const_iterator actual = next;
++next;
while(next != dna_list.end()) // cycle over pairs, dna_list is the set
{
if (similar(*actual, *next))
{
Dna dna_temp(*actual); // copy constructor
dna_list.erase(actual); // erase the old one
do
{
dna_temp.mutate(); // change dna_temp
} while(!dna_list.insert(dna_temp).second); // insert dna_temp
}
++actual;
++next;
}
Sometimes the program can't exit from the main loop. I think the problem happens when I erase the last element in the dna_list. What's the correct way to do this task?
Use actual = next rather than ++actual.
Once you erase actual, it is an invalid iterator, so ++actual will behave strangely. next should remain intact, so assigning actual to next should work.
Your best option is to create a comparison functor that uses the similar() predicate. Then all you need to do is construct the set with that comparison functor and you're done. The set itself will see two similar elements as identical and will only let the first one in.
struct lt_different {
bool operator()(int a, int b) {
return a < b && !similar(a, b);
}
private:
bool similar(int a, int b)
{
// TODO:when are two elements similar?
const int EPSILON = 2;
return abs(a - b) < EPSILON;
}
};
// ...
set<int> o; // fill this set with your data
// copy your data to a new set that rejects similar elements
set<int,lt_different> s(o.begin(), o.end(), lt_different());
You can work with set s: insert elements, remove elements, modify elements -- and the set itself will make sure no two similar elements exist in the set.
That said, you can also write an algorithm yourself, if only for an alternative choice. Take a look at std::adjacent_find() from <algorithm>. It will find the first occurrence of two consecutive identical elements; hold on to that position. With that found, find the first element from that point that is different from these elements. You end up with two iterators that denote a range of consecutive, similar elements. You can use the set's erase() method to remove them, as it has an overload that takes two iterators.
Lather, rinse, repeat for the entire set.