This question already has answers here:
Removing item from vector while iterating?
(8 answers)
Closed 2 years ago.
I'm writing this program why it throws an error in toupper('a')?
void test2(void) {
string n;
vector<string> v;
auto it = v.begin();
do {
cout << "Enter a name of a fruit: ";
cin >> n;
v.push_back(n);
} while (n != "Quit");
v.erase(v.end() - 1);
sort(v.begin(), v.end(), [](string g, string l) { return g < l; });
dis(v);
for (auto i : v) {
if (i.at(0) == toupper('a')) {
cout << i << endl;
v.erase(remove(v.begin(), v.end(), i));
}
}
dis(v);
}
Can someone help me to find the error?
Since you already tried to implement the erase-remove-idiom, that's how it can be used in this case:
v.erase(std::remove_if(v.begin(), v.end(), [](const std::string &item) {
return std::toupper(item.at(0)) == 'A';
}), v.end());
Here I assumed, that i.at(0) == toupper('a') is a typo and should be toupper(i.at(0)) == 'A'.
Write your deletion loop like this:
for ( auto it = std::begin( v ); it != std::end( v ); )
{
if ( toupper( it->at( 0 ) ) == 'A' )
it = v.erase( it );
else
++it;
}
If you do it the way you're doing it you'll invalidate the iterator and then never reassign it a valid iterator which is needed to correctly loop through the vector.
The problem here :
for (auto i : v) {
if (i.at(0) == toupper('a')) {
cout << i << endl;
v.erase(remove(v.begin(), v.end(), i));
}
}
is that you are modifying the vector inside the loop with erase() which invalidates the iterators used internally to implement the for range loop.
The loop is a syntactic sugar for something like this :
{
auto&& range = v;
auto&& first = std::begin(v); // obtained once before entering the loop
auto&& last = std::end(v); // obtained once before entering the loop
for (; first != last; ++first)
{
auto i = *first; // first will be invalid the next time after you call erase()
if (i.at(0) == toupper('a')) {
cout << i << endl;
v.erase(remove(v.begin(), v.end(), i)); // you are invalidating the iterators and then dereferencing `first` iterator at the beginning of the next cycle of the loop
}
}
}
Why calling erase() invalidates the vector ?
This is because a vector is like a dynamic array which stores its capacity (whole array size) and size (current elements count), and iterators are like pointers which point to elements in this array
So when erase() is called it will rearrange the array and decrease its size, so updating the end iterator and your first iterator will not be pointing to the next item in the array as you intended . This illustrates the problem :
std::string* arr = new std::string[4];
std::string* first = arr;
std::string* last = arr + 3;
void erase(std::string* it)
{
std::destroy_at(it);
}
for (; first != last; ++first)
{
if (some_condition)
erase(first); // the last element in the array now is invalid
// thus the array length is now considered 3 not 4
// and the last iterator should now be arr + 2
// so you will be dereferencing a destoryed element since you didn't update your last iterator
}
What to learn from this ?
Never do something which invalidates the iterators inside for range loop.
Solution:
Update iterators at each cycle so you always have the correct bounds :
auto&& range = v;
auto&& first = std::begin(v); // obtained once before entering the loop
auto&& last = std::end(v); // obtained once before entering the loop
for (; first != last;)
{
auto i = *first;
if (i.at(0) == toupper('a'))
{
first = v.erase(remove(v.begin(), v.end(), i));
last = std::end(v);
}
else
{
++first;
}
}
Related
I have this code:
int main()
{
vector<int> res;
res.push_back(1);
vector<int>::iterator it = res.begin();
for( ; it != res.end(); it++)
{
it = res.erase(it);
//if(it == res.end())
// return 0;
}
}
"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."
This code crashes, but if I use the if(it == res.end()) portion and then return, it works. How come? Does the for loop cache the res.end() so the not equal operator fails?
res.erase(it) always returns the next valid iterator, if you erase the last element it will point to .end()
At the end of the loop ++it is always called, so you increment .end() which is not allowed.
Simply checking for .end() still leaves a bug though, as you always skip an element on every iteration (it gets 'incremented' by the return from .erase(), and then again by the loop)
You probably want something like:
while (it != res.end()) {
it = res.erase(it);
}
to erase each element
(for completeness: I assume this is a simplified example, if you simply want every element gone without having to perform an operation on it (e.g. delete) you should simply call res.clear())
When you only conditionally erase elements, you probably want something like
for ( ; it != res.end(); ) {
if (condition) {
it = res.erase(it);
} else {
++it;
}
}
for( ; it != res.end();)
{
it = res.erase(it);
}
or, more general:
for( ; it != res.end();)
{
if (smth)
it = res.erase(it);
else
++it;
}
Because the method erase in vector return the next iterator of the passed iterator.
I will give example of how to remove element in vector when iterating.
void test_del_vector(){
std::vector<int> vecInt{0, 1, 2, 3, 4, 5};
//method 1
for(auto it = vecInt.begin();it != vecInt.end();){
if(*it % 2){// remove all the odds
it = vecInt.erase(it); // note it will = next(it) after erase
} else{
++it;
}
}
// output all the remaining elements
for(auto const& it:vecInt)std::cout<<it;
std::cout<<std::endl;
// recreate vecInt, and use method 2
vecInt = {0, 1, 2, 3, 4, 5};
//method 2
for(auto it=std::begin(vecInt);it!=std::end(vecInt);){
if (*it % 2){
it = vecInt.erase(it);
}else{
++it;
}
}
// output all the remaining elements
for(auto const& it:vecInt)std::cout<<it;
std::cout<<std::endl;
// recreate vecInt, and use method 3
vecInt = {0, 1, 2, 3, 4, 5};
//method 3
vecInt.erase(std::remove_if(vecInt.begin(), vecInt.end(),
[](const int a){return a % 2;}),
vecInt.end());
// output all the remaining elements
for(auto const& it:vecInt)std::cout<<it;
std::cout<<std::endl;
}
output aw below:
024
024
024
A more generate method:
template<class Container, class F>
void erase_where(Container& c, F&& f)
{
c.erase(std::remove_if(c.begin(), c.end(),std::forward<F>(f)),
c.end());
}
void test_del_vector(){
std::vector<int> vecInt{0, 1, 2, 3, 4, 5};
//method 4
auto is_odd = [](int x){return x % 2;};
erase_where(vecInt, is_odd);
// output all the remaining elements
for(auto const& it:vecInt)std::cout<<it;
std::cout<<std::endl;
}
Something that you can do with modern C++ is using "std::remove_if" and lambda expression;
This code will remove "3" of the vector
vector<int> vec {1,2,3,4,5,6};
vec.erase(std::remove_if(begin(vec),end(vec),[](int elem){return (elem == 3);}), end(vec));
The it++ instruction is done at the end of the block. So if your are erasing the last element, then you try to increment the iterator that is pointing to an empty collection.
Do not erase and then increment the iterator. No need to increment, if your vector has an odd (or even, I don't know) number of elements you will miss the end of the vector.
You increment it past the end of the (empty) container in the for loop's loop expression.
The following also seems to work :
for (vector<int>::iterator it = res.begin(); it != res.end(); it++)
{
res.erase(it--);
}
Not sure if there's any flaw in this ?
if(allPlayers.empty() == false) {
for(int i = allPlayers.size() - 1; i >= 0; i--)
{
if(allPlayers.at(i).getpMoney() <= 0)
allPlayers.erase(allPlayers.at(i));
}
}
This works for me. And Don't need to think about indexes have already erased.
As a modification to crazylammer's answer, I often use:
your_vector_type::iterator it;
for( it = res.start(); it != res.end();)
{
your_vector_type::iterator curr = it++;
if (something)
res.erase(curr);
}
The advantage of this is that you don't have to worry about forgetting to increment your iterator, making it less bug prone when you have complex logic. Inside the loop, curr will never be equal to res.end(), and it will be at the next element regardless of if you erase it from your vector.
I have this code:
int main()
{
vector<int> res;
res.push_back(1);
vector<int>::iterator it = res.begin();
for( ; it != res.end(); it++)
{
it = res.erase(it);
//if(it == res.end())
// return 0;
}
}
"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."
This code crashes, but if I use the if(it == res.end()) portion and then return, it works. How come? Does the for loop cache the res.end() so the not equal operator fails?
res.erase(it) always returns the next valid iterator, if you erase the last element it will point to .end()
At the end of the loop ++it is always called, so you increment .end() which is not allowed.
Simply checking for .end() still leaves a bug though, as you always skip an element on every iteration (it gets 'incremented' by the return from .erase(), and then again by the loop)
You probably want something like:
while (it != res.end()) {
it = res.erase(it);
}
to erase each element
(for completeness: I assume this is a simplified example, if you simply want every element gone without having to perform an operation on it (e.g. delete) you should simply call res.clear())
When you only conditionally erase elements, you probably want something like
for ( ; it != res.end(); ) {
if (condition) {
it = res.erase(it);
} else {
++it;
}
}
for( ; it != res.end();)
{
it = res.erase(it);
}
or, more general:
for( ; it != res.end();)
{
if (smth)
it = res.erase(it);
else
++it;
}
Because the method erase in vector return the next iterator of the passed iterator.
I will give example of how to remove element in vector when iterating.
void test_del_vector(){
std::vector<int> vecInt{0, 1, 2, 3, 4, 5};
//method 1
for(auto it = vecInt.begin();it != vecInt.end();){
if(*it % 2){// remove all the odds
it = vecInt.erase(it); // note it will = next(it) after erase
} else{
++it;
}
}
// output all the remaining elements
for(auto const& it:vecInt)std::cout<<it;
std::cout<<std::endl;
// recreate vecInt, and use method 2
vecInt = {0, 1, 2, 3, 4, 5};
//method 2
for(auto it=std::begin(vecInt);it!=std::end(vecInt);){
if (*it % 2){
it = vecInt.erase(it);
}else{
++it;
}
}
// output all the remaining elements
for(auto const& it:vecInt)std::cout<<it;
std::cout<<std::endl;
// recreate vecInt, and use method 3
vecInt = {0, 1, 2, 3, 4, 5};
//method 3
vecInt.erase(std::remove_if(vecInt.begin(), vecInt.end(),
[](const int a){return a % 2;}),
vecInt.end());
// output all the remaining elements
for(auto const& it:vecInt)std::cout<<it;
std::cout<<std::endl;
}
output aw below:
024
024
024
A more generate method:
template<class Container, class F>
void erase_where(Container& c, F&& f)
{
c.erase(std::remove_if(c.begin(), c.end(),std::forward<F>(f)),
c.end());
}
void test_del_vector(){
std::vector<int> vecInt{0, 1, 2, 3, 4, 5};
//method 4
auto is_odd = [](int x){return x % 2;};
erase_where(vecInt, is_odd);
// output all the remaining elements
for(auto const& it:vecInt)std::cout<<it;
std::cout<<std::endl;
}
Something that you can do with modern C++ is using "std::remove_if" and lambda expression;
This code will remove "3" of the vector
vector<int> vec {1,2,3,4,5,6};
vec.erase(std::remove_if(begin(vec),end(vec),[](int elem){return (elem == 3);}), end(vec));
The it++ instruction is done at the end of the block. So if your are erasing the last element, then you try to increment the iterator that is pointing to an empty collection.
Do not erase and then increment the iterator. No need to increment, if your vector has an odd (or even, I don't know) number of elements you will miss the end of the vector.
You increment it past the end of the (empty) container in the for loop's loop expression.
The following also seems to work :
for (vector<int>::iterator it = res.begin(); it != res.end(); it++)
{
res.erase(it--);
}
Not sure if there's any flaw in this ?
if(allPlayers.empty() == false) {
for(int i = allPlayers.size() - 1; i >= 0; i--)
{
if(allPlayers.at(i).getpMoney() <= 0)
allPlayers.erase(allPlayers.at(i));
}
}
This works for me. And Don't need to think about indexes have already erased.
As a modification to crazylammer's answer, I often use:
your_vector_type::iterator it;
for( it = res.start(); it != res.end();)
{
your_vector_type::iterator curr = it++;
if (something)
res.erase(curr);
}
The advantage of this is that you don't have to worry about forgetting to increment your iterator, making it less bug prone when you have complex logic. Inside the loop, curr will never be equal to res.end(), and it will be at the next element regardless of if you erase it from your vector.
I'm trying to remove duplicate combinations of integer vectors stored in a list using a hash table. Iterating over each integer vector in the list, I:
Calculate the hash_value (thash)
See if the hash value is already in the hash table (pids)
If it's in the hash table, erase that vector from the list.
Otherwise, add that value to the hash_table and increment the list
iterator
Print statements seem to confirm my logic, but the loop hangs at the fourth step of iteration. I've commented the it++ and vz.remove(it) that cause the problem and only show the logic in the code below. The code is also available through ideone: https://ideone.com/JLGA0f
#include<iostream>
#include<vector>
#include<list>
#include<cmath>
#include<unordered_set>
using namespace std;
double hash_cz(std::vector<int> &cz, std::vector<double> &lprimes) {
double pid = 0;
for(auto it = cz.begin(); it != cz.end(); it++) {
pid += lprimes[*it];
}
return pid;
}
int main(){
// create list of vectors
std::list<std::vector<int>> vz;
vz.push_back({2,1});
vz.push_back({1,2});
vz.push_back({1,3});
vz.push_back({1,2,3});
vz.push_back({2, 1});
// vector of log of prime numbers
std::vector<double> lprimes {2, 3, 5, 7};
for (auto it = lprimes.begin(); it != lprimes.end(); it++) {
*it = std::log(*it);
}
std::unordered_set<double> pids;
double thash;
for (auto it = vz.begin(); it != vz.end(); ) {
thash = hash_cz(*it, lprimes);
std::cout << thash << std::endl;
// delete element if its already been seen
if (pids.find(thash) != pids.end()) {
std::cout << "already present. should remove from list" << std::endl;
// vz.erase(it);
}
else {
// otherwise add it to hash_table and increment pointer
std::cout << "not present. add to hash. keep in list." << std::endl;
pids.insert(thash);
// it++;
}
it++;
}
for (auto it = vz.begin(); it != vz.end(); it++) {
for (auto j = it -> begin(); j != it -> end(); j++) {
std::cout << *j << ' ';
}
std::cout << std::endl;
}
return 0;
}
Problem is this line of code:
vz.erase(it);
It keeps iterator where it was ie leaves it invalid. It should be either:
vz.erase(it++);
or
it = vz.erase( it );
Note: std::unoredered_set::insert() return value tells you if insert was succesfull or not (if the same value element is there already), you should call it and check result. In your code you do lookup twice:
if (pids.insert(thash).second ) {
// new element added
++it;
} else {
// insertion failed, remove
it = vz.erase( it );
}
As std::list provides remove_if() your code can be simplified:
vz.remove_if( [&pids,&lprimes]( auto &v ) {
return !pids.insert( hash_cz(v, lprimes) ).second );
} );
instead of whole loop.
If the element has already been seen, you erase() the it node and then increment it at the end of the loop: undefined behaviour. Try erase(it++) instead.
If the element has not been seen, you increment it and then do it again at the end of for, yielding UB if it was end() - 1 as it moves past end.
Here i am trying to print the frequency of each word in the sentence, which is stored in the vector of string
void display_by_word (vector<string> vs) //pass by value is necessary because we need to delete the elements.
{
vector<string> :: size_type vec_size, i;
string to_cmp = vs[0];
int occ = 0;
for ( i = 0; i < vs.size(); ++i){
vector <string> :: iterator it = vs.begin() + 1;
occ = 1;
for ( it ; it != vs.end(); ++it){
if ( vs[i] == *it){
vs.erase(it);
occ++;
}
}
cout << vs[i] << " " << occ << endl;
}
}
Sometimes it works fine but sometimes it crashes.what is wrong?
See http://en.cppreference.com/w/cpp/container/vector/erase
Invalidates iterators and references at or after the point of the erase [...]
After the erase has happened, you cannot reuse it because it has been invalidated. It's undefined behaviour, which can include random crashes.
However, erase returns an iterator to the element following the erased one, or to end() if it was the last element, which is why the solution with it = vs.erase(it); works.
Alternatively, consider using std::remove_if, followed by the two-argument erase, which is known as the Erase-Remove Idiom. It may turn out to be more elegant and more readable than a hand-written loop. Or just rewrite the whole function to use std::count_if.
You may want to rewrite the loop as something like this
while(it != vs.end())
{
if ( vs[i] == *it){
it = vs.erase(it);
occ++;
}
else
it++;
}
You may should do as below:
`
for ( it ; it != vs.end(); ){
if ( vs[i] == *it){
it = vs.erase(it);
occ++;
}
else{
++it;
}
}
`
I am a little confused by the way begin and end work they seem to me to be inconsistant. When going forward and backwards they have different behaviors.
vector<Actor *> a;
a.push_back(new Actor(11));
a.push_back(new Actor(22));
a.push_back(new Actor(33));
vector<Actor *>::iterator it = a.begin();
int x =0;
while(a.begin()+x != a.end()){
cout << (*(a.begin()+x)) << "\n";
x++;
}
cout << "\n";
int y = 1; // if this is set to 0 then its a seg fault =/ when I access
while(a.end()-y != a.begin()){
cout << (*(a.end()-y)) << "\n";
y++;
}
Outputs
0x979a008
0x979a028
0x979a018
0
0x979a018
0x979a028
How can I get the expected pattern
0x979a008
0x979a028
0x979a018
0x979a018
0x979a028
0x979a008
Note that begin() points to the first element of the vector, but end() points past the last element. It's never safe to dereference end(), but you can compare iterators to it.
If the vector is empty, then begin() == end(), and you may not dereference either one.
A more idiomatic way to loop over a vector's elements is:
for (vector<Actor*>::iterator i = a.begin(); i != a.end(); ++i) {
// do something here
}
To iterate in reverse, it's simpler to use rbegin() and rend(), which work much the same way and begin()/end(), but iterate in reverse order:
for (vector<Actor*>::reverse_iterator i = a.rbegin(); i != a.rend(); ++i) {
// do something here
}
Also, if you don't intend to modify the elements, you should use a const_iterator (or const_reverse_iterator instead.
You should use reverse iterators:
int y = 0;
while(a.rbegin() +y != a.rend()){
cout << (*(a.rbegin()+y)) << "\n";
y++;
}
Or even better would be to use the overloaded ++ operator of the iterators themselves:
auto iter = a.rbegin();
while(iter != a.rend()){
cout << *(iter++) << "\n";
}
One very simple way to achieve that would be following
// first element to the last
auto it = a.begin()
while (it != a.end())
{
cout<<*it<<"\n";
++it;
}
cout<<"\n"
// Last element to first
auto rit = a.rbegin()
while(rit != a.rend())
{
cout<<*rit<<"\n";
++rit;
}
NB: Do not try to dereference a.end() and beyond. When y = 0 in your program the a.end() is dereferenced in the line cout << (*(a.end()-y)) << "\n"; This results in seg fault.
Elements of vector are contained in a sequence which can be accessed from begin() through end()-1. .end() points to one "past" the last element of the container and should not be dereferenced.
std::for_each(a.begin(), a.end(), [](const Actor *& a){ std::cout << a; });
std::for_each(a.rbegin(), a.rend(), [](const Actor *& a){ std::cout << a; });
auto print_actor = [](const Actor *& a){ std::cout << a; };
std::for_each(a.begin(), a.end(), print_actor);
std::for_each(a.rbegin(), a.rend(), print_actor);