How to iterate std::set? - c++

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

Related

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

How to return an iterator from a function including the end case

In c++ I need to search a vector containing a pair, in reverse, by the string. I cannot use a map because the strings are not unique and order is important. I then want to return a forward iterator if the string is found or the end iterator if the string is not found.
Please see below for my current code. I have no problem when the string is found but, if the string is not found, I get a segfault in the conditional statement in main.
vector<pair<string, int>>::iterator prev_it(const string& pred,
vector<pair<string, int>> prevpreds) {
vector<pair<string, int>>::reverse_iterator rit;
for(rit = prevpreds.rbegin();
rit != prevpreds.rend(); ++rit) {
if (rit->first == pred) {
return (rit+1).base();}
}
if(rit == prevpreds.rend()) {
return prevpreds.end();
}
}
and in main:
int main() {
vector<pair<string, int>> test;
for(int i = 0; i <= 5; ++i) {
pair<string, int> mypair;
mypair = make_pair("X"+to_string(i%4+1), i+1);
test.emplace_back(mypair);
}
string tpred = "X"+to_string(6);
vector<pair<string, int>>::iterator tit;
tit = prev_it(tpred, test);
if (tit != test.end()) {
cout << tit->first << " " << tit->second << endl;
}
else {cout << "This is the end." << endl;}
}
The code works if tpred is one of X1 to X4. If tpred is X6 (i.e. not an element of test) then I get a segfault. What I would like to be able to do is return the end forward iterator and then, as in main(), have a conditional based on this.
Edit: I am new to c++ (about a year). I am returning a forward iterator because I need to use the iterator later and this seems clearer (but I could be wrong). As far as I understand, a multimap allows non-unique keys but will order the unique keys. I should have been clearer and said time order was important, not key order. I prefer not to use auto while developing because I like to see what I container element/iterator I am using, but point taken.
You're using an iterator of a destructed object. Pass prevpreds by reference, so the iterator maintains valid.
vector<pair<string, int>>::const_iterator prev_it(const string& pred,
const vector<pair<string, int>> &prevpreds)
{
vector<pair<string, int>>::const_reverse_iterator rit;
for (rit = prevpreds.rbegin();
rit != prevpreds.rend(); ++rit)
{
if (rit->first == pred)
{
return (rit + 1).base();
}
}
return prevpreds.end();
}
int main()
{
// ...
vector<pair<string, int>>::const_iterator tit; // <-- uses const iterator
tit = prev_it(tpred, test);
// ...
}

inconsistent behavior with erasing map iterators in C++

I'm very confused by the behavior of the erase function for maps. In the simple example below, the code outputs "224". However, if you comment out the line "m['e'] = 5", it outputs "221". Neither result makes sense to me. Can someone explain the logic here?
#include <iostream>
#include <map>
using namespace std;
int main(){
map<char, int> m;
m['a'] = 1;
m['b'] = 2;
m['c'] = 3;
m['d'] = 4;
m['e'] = 5;
map<char, int>::iterator it = m.begin(); it++;
cout << it->second;
m.erase(it);
cout << it->second;
it++;
cout << it->second << endl;
}
You cannot use an iterator after erasing it. It is invalid and the behaviour is not determined (crash, wriong value ?):
http://en.cppreference.com/w/cpp/container/map/erase
map<char, int>::iterator it = m.begin(); it++;
cout << it->second;
m.erase(it); // (1)
cout << it->second; // (2)
it++; // (3)
cout << it->second << endl;
You have invalidated the iterator at position (1), so attempting to dereference it in (2) and (3) is undefined behavior. It is virtually identical to deleting a pointer and then then attempting to dereference it.
To erase elements in map for pre c++11 you can use this pattern:
for( map_type::iterator it = map.begin(); it != map.end(); ) {
if( condition_to_erease ) map.erase( it++ );
else ++i;
}
Why map.erase( it++ ); works? Because it is basically equivalent to this code:
map::iterator tmp = it;
++it;
map.erase( tmp );
You should understand semantics of postfics/prefics operators if you want to use C++ effectively.
For c++11 you can also use this:
for( auto it = map.begin(); it != map.end(); ) {
if( condition_to_erease ) it = map.erase( it );
else ++i;
}
I think in Visual C++ std::map::erase() also returned iterator, but that was not in standard.

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

Better way to iterate std::map

Given a map, I need to retrieve and operate two immediately stored items.
To me, working on a vector is litter easier since I can do "iter + 1" or "iter - 1".
While for map, I am out of luck.
For example, I give a simple example as follows:
Note: in my real application, I don't simply subtract those numbers.
int main ()
{
map<char,int> mymap;
map<char,int>::iterator it;
mymap['b'] = 100;
mymap['a'] = 200;
mymap['c'] = 300;
// show content:
map<char,int>::iterator firstItem = mymap.begin();
map<char,int>::iterator secondItem = ++mymap.begin();
for ( ; secondItem != mymap.end(); ++firstItem, ++secondItem )
cout << secondItem->second - firstItem->second << endl;
return 0;
}
Question> Is there a better solution for this?
Thank you
Instead of incrementing both iterators in the loop control (incrementing is a bit slow), just assign firstItem = secondItem then increment secondItem.
You can do it with a single iterator. Move the increment from the header to the middle of your loop, and exit the loop when you hit the end of your map, like this:
map<char,int>::iterator item = mymap.begin();
for (;;) {
int first = item->second;
++item;
if ( item == mymap.end()) break;
cout << item->second - first << endl;
}
This is a matter of style. You can do eg.
auto first = m.begin();
if (first != m.end())
{
auto second = first;
second++;
for (; second != m.end(); first = second++)
{
...
}
}
You can also bailout more elegantly in the case where the map is empty. For instance you can do:
if (m.empty()) return;
auto first = m.begin(), second = first;
for (second++; second != m.end(); first = second++)
{
...
}
I'd favor the latter if I can, and use the former only if I must.
Your current loop will show undefined behaviour if the map is empty.
Your loop could be rewritten (more simply, and checking for an empty map) like so:
int main(int argc, char * argv[])
{
map<char,int> mymap;
map<char,int>::iterator it;
mymap['b'] = 100;
mymap['a'] = 200;
mymap['c'] = 300;
for ( it = ( mymap.begin() == mymap.end() ? mymap.end() : std::next(mymap.begin()) ) ; it != mymap.end(); ++it )
cout << it->second - std::prev(it)->second << endl;
return 0;
}
Your code will have undefined behavior if the map is empty but other than that it seems to be a reasonable approach, depending on your overall goal. Since map iterators are not random access you can't just add or subtract one, only increment/decrement.
An alternate approach is to make a copy of the iterator and then incrementing inside the loop.
Neither better, nor worse, just an alternative:
if (map.size() >=2)
std::accumulate(
++mymap.begin(),
mymap.end(),
mymap.begin(),
[](mymap_type::const_iterator iprev, mymap_type::value_type const& entry)->mymap_type::const_iterator
{
/* do something */;
return ++iprev;
});