vector iterators c++ - c++

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);

Related

How to read data from a Vector

How can I use the following vector to read true/false from using a while or for loop.
With this implemtation of the loop I get an error for the oprator !=
no operator "!=" matches these operands
vector<bool> Verification;
Verification.push_back(true);
Verification.push_back(false);
Verification.push_back(true);
Verification.push_back(false);
Verification.push_back(true);
for (int it = Verification.begin(); it != Verification.end(); it++) {
if (it==true) cout<<"true";
else if (it == false) cout<<"false";
}
You are declaring it as the wrong type. The result of Verification.begin() is a std::vector<bool>::iterator. But you don't need to specify that.
Use a range-for loop instead
for (bool b : Verification)
{
std::cout << std::boolalpha << b;
}
There are various ways to iterate over an std::vector
Using iterator
Long example:
for( std::vector<bool>::iterator it = v.begin(); it != v.end(); ++it ) std::cout << *it;
or the same but shorter:
for( auto it = v.begin(); it != v.end(); ++it ) std::cout << *it;
Using index
Here:
for( unsigned int i = 0; i != v.size(); ++i ) std::cout << v[i];
Range loop
Here:
for( bool b : v ) std::cout << b;
(there are some more but we will omit them for clarity)
Looks like you mixed 1 and 2 hense you have compilation errors. Choose one.
The problem is that Verification.begin() gives you an iterator while it is an int.
To solve this you could modify your for loop to:
for (std::vector<bool>::iterator it = Verification.begin(); it != Verification.end(); it++) {
if (*it==true) cout<<"true";
else if (*it == false) cout<<"false";
}
Note *it means we're dereferencing the iterator it and then comparing the result.
Also you don't need the else if because you can just use else.
Alternative solution
You can also use a range-base for loop as shown below:
for (bool element : Verification)
{
std::cout << std::boolalpha << element;
}

memory out of range Vector [duplicate]

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;
}
}

loop hangs for iterated std::erase on std::list

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.

Iterating multimap with conditional statement

First of all, I'm using C++98/03
I'm iterating my multimap starting from the second element:
multimap<pair<string, string>, pair<string, int> >::iterator it = paths.begin();
it++;
I have a conditional statement: if first element of first pair in current iterator is equal to the first element of first pair in a previous iterator, then do something, eg. print these elements.
for(; it != paths.end(); it++) {
if((*it).first.first == (*it--).first.first ) {
it++;
cout << (*it).first.first << " ";
cout << (*it--).first.first << endl;
it++;
}
else {
it++;
}
}
My question is how can I use a copy of an iterator instead of incrementing it back after every (*it--)?
Create an utility similar to C++11's std::prev:
#include <algorithm>
template <class T>
T prev(T it)
{
std::advance(it, -1);
return it;
}
Then use it as follows:
for(; it != paths.end(); it++) {
if((*it).first.first == prev(it)->first.first ) {
cout << (*it).first.first << " ";
cout << prev(it)->first.first << endl;
}
else {
it++;
}
}
Just use another iterator:
typedef multimap<pair<string, string>, pair<string, int> >::iterator iterator;
for( iterator it = paths.begin(); it != paths.end(); ) {
iterator prev = it++;
if( it == paths.end() )
break;
if( prev->first.first == it->first.first ) {
// output here
}
}
Note your code is incorrect, first of all it has UB as == is not sequenced. But even if you use different iterator on the left side, you would get wrong behaviour:
iterator it1 = it;
if((*it1).first.first == (*it--).first.first ) { // not UB anymore, but result is always true as you compare the same element

How to iterate std::set?

I have this code:
std::set<unsigned long>::iterator it;
for (it = SERVER_IPS.begin(); it != SERVER_IPS.end(); ++it) {
u_long f = it; // error here
}
There is no ->first value.
How I can obtain the value?
You must dereference the iterator in order to retrieve the member of your set.
std::set<unsigned long>::iterator it;
for (it = SERVER_IPS.begin(); it != SERVER_IPS.end(); ++it) {
u_long f = *it; // Note the "*" here
}
If you have C++11 features, you can use a range-based for loop:
for(auto f : SERVER_IPS) {
// use f here
}
Another example for the C++11 standard:
set<int> data;
data.insert(4);
data.insert(5);
for (const int &number : data)
cout << number;
Just use the * before it:
set<unsigned long>::iterator it;
for (it = myset.begin(); it != myset.end(); ++it) {
cout << *it;
}
This dereferences it and allows you to access the element the iterator is currently on.
How do you iterate std::set?
int main(int argc,char *argv[])
{
std::set<int> mset;
mset.insert(1);
mset.insert(2);
mset.insert(3);
for ( auto it = mset.begin(); it != mset.end(); it++ )
std::cout << *it;
}
One more thing that might be useful for beginners is , since std::set is not allocated with contiguous memory chunks , if someone want to iterate till kth element normal way will not work.
example:
std::vector<int > vec{1,2,3,4,5};
int k=3;
for(auto itr=vec.begin();itr<vec.begin()+k;itr++) cout<<*itr<<" ";
std::unordered_set<int > s{1,2,3,4,5};
int k=3;
int index=0;
auto itr=s.begin();
while(true){
if(index==k) break;
cout<<*itr++<<" ";
index++;
}