C++ iterator behaviour in for vs while loops - c++

I do not understand why iterating through a container with a for loop produces different results than iterating through it with a while loop. The following MWE illustrates this with a vector and a set of 5 integers.
#include <iostream>
#include <vector>
#include <set>
using namespace std;
int main()
{
vector<int> v;
set<int> s;
// add integers 0..5 to vector v and set s
for (int i = 0; i < 5; i++) {
v.push_back(i);
s.insert(i);
}
cout << "Iterating through vector with for loop.\n";
vector<int>::const_iterator itv;
for (itv = v.begin(); itv != v.end(); itv++) cout << *itv << ' ';
cout << '\n';
cout << "Iterating through set with for loop.\n";
set<int>::const_iterator its;
for (its = s.begin(); its != s.end(); its++) cout << *its << ' ';
cout << '\n';
cout << "Iterating through vector with while loop.\n";
itv = v.begin();
while (itv++ != v.end()) cout << *itv << ' ';
cout << '\n';
cout << "Iterating through set with while loop.\n";
its = s.begin();
while (its++ != s.end()) cout << *its << ' ';
cout << '\n';
}
The above produces:
Iterating through vector with for loop.
0 1 2 3 4
Iterating through set with for loop.
0 1 2 3 4
Iterating through vector with while loop.
1 2 3 4 0
Iterating through set with while loop.
1 2 3 4 5
The for loops work as expected but not the while loops. Since I'm using ++ as a postfix, I don't understand why the while loops behave as they do. Another mystery is why the while loop prints a 5 for set s, since this number was not inserted in s.

Your while loop is not equivalent to the for loop.
The for loop is equivalent to
itv = v.begin();
while(itv != v.end()) {
cout << *itv << ' ';
itv++;
}
Note that the increment happens after the cout. In your while loops, you do the increment in the test, before cout. Even though you use postincrement, the increment takes effect before your loop body is executed.
Write your while loops like I did there and the discrepancy should disappear.

When you iterate using the for loop you increment the iterator only after the body is evaluated. When you iterate using the while loop you increment the iterator after the check but before the body of the loop. Dereferencing the iterator in the last iteration of your while loops causes undefined behavior.

It could be because the compiler evaluates the its++ in the while expression first before evaluating the rest of the expression.

Since I'm using ++ as a postfix, I don't understand why the while loops behave as they do.
That's because first the while predicate is evaluated, and then (if the predicate was true) the body of the while loop. By the time you try to access the value in the body, the iterator was already incremented.

Just a few 'random' style hints, mainly showing algorithm use and modern C++11 features.
#include <iostream>
#include <vector>
#include <set>
#include <algorithm>
#include <iterator>
int main()
{
const std::vector<int> v { 0,1,2,3,4 };
const std::set<int> s { 0,1,2,3,4 };
for (auto element : v)
std::cout << element << ' ';
std::cout << '\n';
for (auto element : s)
std::cout << element << ' ';
std::cout << '\n';
std::copy(v.begin(), v.end(), std::ostream_iterator<int>(std::cout, " "));
std::cout << '\n';
std::copy(s.begin(), s.end(), std::ostream_iterator<int>(std::cout, " "));
std::cout << '\n';
}

Related

About iterator of containers

I do not know, why does it output 1024?
vector<int> default_container = { 1,2,3,4,5,6,7,78,8,1024 };
cout << *default_container.end() << endl; // 0
default_container.pop_back();
for (auto it : default_container)
{
cout << it << ",";
}
cout << endl;
cout << *default_container.end() << endl; // 1024 why?why?why?why?
cout << *--default_container.end() << endl; // 8
Your program has Undefined behavior!
You are de-referencing the end iterator, at the lines
cout << *default_container.end() << endl;
...
cout << *default_container.end() << endl;
which gives you undefined behavior. Form cppreference.com the std::vector::end, std::vector::cend
Returns an iterator to the element following the last element of the vector.
This element acts as a placeholder; attempting to access it results in undefined behavior.
Means, anything can be happened; therefore you shouldn't be relaying on its result and should not be doing it!
That being said, it looks like that you want to access the last element in the vector. If that the case, for a non-empty vector you have multiple other (safe) choices:
Using std::vector::back
// prints first 8 from last; due to "default_container.pop_back()"
std::cout << default_container.back();
Using std::prev
#include <iterator>
// prints first 8 from last; due to "default_container.pop_back()"
std::cout << *std::prev(default_container.end());
Using reverse iterator std::rbegin
// prints first 8 from last; due to "default_container.pop_back()"
std::cout << *std::rbegin(default_container);
As a side note, see: Why is "using namespace std;" considered bad practice?
The previous answer says it all.
In other words you're not supposed to use end() for anything else then comparing an iterator to.
Eg.
for (auto it = container.begin(); it < container.end(); ++it)
Also note that your line for (auto it : default_container)
isn't creating an iterator but literally an int.
// foreach value in container
for(int value : default_container)
{
cout << value;
}
note that if you're not planning to change the values you're iterating over you can use this :
for(const auto value : default_container) {}
or if your containter contains objects (instance of classes)
for(const auto& object : container) {}

C++ finding uint8_t in vector<uint8_t>

I have the following simple code. I declare a vector and initialize it with one value 21 in this case. And then i am trying to find that value in the vector using find. I can see that the element "21" in this case is in the vector since i print it in the for loop. However why the iterator of find does not resolve to true?
vector<uint8_t> v = { 21 };
uint8_t valueToSearch = 21;
for (vector<uint8_t>::const_iterator i = v.begin(); i != v.end(); ++i){
cout << unsigned(*i) << ' ' << endl;
}
auto it = find(v.begin(), v.end(), valueToSearch);
if ( it != v.end() )
{
string m = "valueToSearch was found in the vector " + valueToSearch;
cout << m << endl;
}
are you sure it doesn't work?
I just tried it:
#include<iostream> // std::cout
#include<vector>
#include <algorithm>
using namespace std;
int main()
{
vector<uint8_t> v = { 21 };
uint8_t valueToSearch = 21;
for (vector<uint8_t>::const_iterator i = v.begin(); i != v.end(); ++i){
cout << unsigned(*i) << ' ' << endl;
}
auto it = find(v.begin(), v.end(), valueToSearch);
if ( it != v.end() )
{// if we hit this condition, we found the element
string error = "valueToSearch was found in the vector ";
cout << error << int(valueToSearch) << endl;
}
return 0;
}
There are two small modifications:
in the last lines inside the "if", because you cannot add directly a
number to a string:
string m = "valueToSearch was found in the vector " + valueToSearch;
and it prints:
21
valueToSearch was found in the vector 21
while it's true that you cannot add a number to a string, cout
support the insertion operator (<<) for int types, but not uint8_t,
so you need to convert it to it.
cout << error << int(valueToSearch) << endl;
This to say that the find is working correctly, and it is telling you that it found the number in the first position, and for this, it != end (end is not a valid element, but is a valid iterator that marks the end of your container.)
Try it here

std::vector<int> allocated with (n,0) is not iterable

Consider the following code:
#include <vector>
#include <iostream>
void monitor_vector(std::vector<int> myV)
{
std::vector<int>::iterator it = myV.begin();
std::cout << "Vector size: " << myV.size() << std::endl;
while (*it)
{
std::cout << "Element " << it-myV.begin() << ": " << *it << std::endl;
it++;
}
}
int main()
{
std::vector<int> myVector(4,1);
monitor_vector(myVector);
return 0;
}
Basically, I create a std::vector<int> that has 4 elements, and assign each element 1. When I execute this code, all is well. The output shows each element.
However, when I execute it with this line
std::vector<int> myVector(4,1);
replaced by this line:
std::vector<int> myVector(4,0);
I can't see any output. The output is:
yilmazali#yilmazali:~/cpp_practice$ g++ -o la vector_practice.cpp
yilmazali#yilmazali:~/cpp_practice$ ./la
Vector size: 4
yilmazali#yilmazali:~/cpp_practice$
Why does it ignore the elements with default integer value? Still, they are there as the member of std::vector.
Thanks for your time,
Ali
You iterate while (*it).
If you fill your vector with zeros, *it == 0 and therefore if (*it) is like if (false): the loop body is never executed.
Instead, loop while it != myV.end()

Alternative to range-based for loops in c++

Is there any alternative to a range-based for loop when it comes to vector arrays? I've noticed that c++98 won't allow range-based for loops. Here is some sample code looping through a vector array using a range based for loop:
#include <iostream>
#include <vector>
using namespace std;
int main() {
vector<int> vecArray1 (3,20);
for (int v: vecArray1) {
cout << "ArrayValue:" << v << endl;
}
return 0;
}
Now here is an alternative I've tried that didn't work:
#include <iostream>
#include <vector>
using namespace std;
int main() {
vector<int> vecArray1 (3,20);
for (int i=0; i<sizeof(vecArray1); ++i) {
cout << "ArrayValue:" << vecArray1[i] << endl;
}
return 0;
}
It outputs 10 elements instead of 3 defined by the vector array above. Are there any alternatives to range-based for loops that work with c++98?
C++98 does not allow for range-based for-loops.
In C++98 you would need to do the following:
for(unsigned int i=0;i<vecArray.size();++i)
std::cout << "ArrayValue: " << vecArray[i] << std::endl;
or
for(std::vector<int>::iterator it=vecArray.begin();it!=vecArray.end();++it)
std::cout << "ArrayValue: " << *it << std::endl;
The operator
sizeof
does NOT give you the length of an array. Instead, it returns an unsigned integer representing the number of bytes of the type you give as the argument.
For example,
std::cout << sizeof(unsigned long long) << std::endl;
prints
8
on my machine, because the type unsigned long long consista of 64-bits, or 64/8 = 8 bytes.
In C++11 we now have range-based for-loops:
Examples:
for(int i : vecArray)
std::cout << "i = " << i << std::endl;
for(const int& i : vecArray)
std::cout << "i = " << i << std::endl;
In the first example the values are copied from vecArray into the variable i. In the second example you are instead working with const references to the values in vecArray (which could be useful for objects which are expensive to copy).
In fact, the range-based for loops in C++11 are available for all types on which you can call begin() and end() (i.e. those which you can iterate through).
sizeof(vecArray1) doesn't do what you think it does.
Either use:
for (int i=0; i<vecArray1.size(); ++i) {
cout << "ArrayValue:" << vecArray1[i] << endl;
}
or:
for (std::vector<int>::iterator it = vecArray1.begin(); it != vecArray1.end(); it++) {
cout << "ArrayValue: " << *it << endl;
}
The second one is more verbose, but works for other types of collections as well (like std::list) and is more similar to what the range-based for loop does.

unordered_multimap - iterating the result of find() yields elements with different value

The multimap in C++ seems to work really odd, i would like to know why
#include <iostream>
#include <unordered_map>
using namespace std;
typedef unordered_multimap<char,int> MyMap;
int main(int argc, char **argv)
{
MyMap map;
map.insert(MyMap::value_type('a', 1));
map.insert(MyMap::value_type('b', 2));
map.insert(MyMap::value_type('c', 3));
map.insert(MyMap::value_type('d', 4));
map.insert(MyMap::value_type('a', 7));
map.insert(MyMap::value_type('b', 18));
for(auto it = map.begin(); it != map.end(); it++) {
cout << it->first << '\t';
cout << it->second << endl;
}
cout << "all values to a" << endl;
for(auto it = map.find('a'); it != map.end(); it++) {
cout << it->first << '\t' << it->second << endl;
}
}
this is the output:
c 3
d 4
a 1
a 7
b 2
b 18
all values to a
a 1
a 7
b 2
b 18
why does the output still contain anything with b as the key when I am explicitly asking for 'a'? Is this a compiler or stl bug?
find, as implemented, returns an iterator for the first element which matches the key in the multimap (as with any other map). You're likely looking for equal_range:
// Finds a range containing all elements whose key is k.
// pair<iterator, iterator> equal_range(const key_type& k)
auto its = map.equal_range('a');
for (auto it = its.first; it != its.second; ++it) {
cout << it->first << '\t' << it->second << endl;
}
That's not a bug, it is by design. find returns an iterator to one of the matching elements, that's all. You'll iterate to the end of the map with your construct.
You need to use multimap::equal_range to do what you are after.
There is an Example in www.cplusplus.com , about How to use equal_range method to get all elements having the same key.
// unordered_multimap::equal_range
#include <iostream>
#include <string>
#include <unordered_map>
#include <algorithm>
typedef std::unordered_multimap<std::string,std::string> stringmap;
int main ()
{
stringmap myumm = {
{"orange","FL"},
{"strawberry","LA"},
{"strawberry","OK"},
{"pumpkin","NH"}
};
std::cout << "Entries with strawberry:";
auto range = myumm.equal_range("strawberry");
for_each (
range.first,
range.second,
[](stringmap::value_type& x){std::cout << " " << x.second;}
);
return 0;
}
Please reference the link : http://www.cplusplus.com/reference/unordered_map/unordered_multimap/equal_range/
It would seem that you get an iterator into the full "list" of pairs, starting at the first pair with 'a' as it's key. So when you iterate to the end, naturally you will get everything beyond 'a' as well. If you sought for 'c', you would probably iterate through the entire "list" doing what you do there. Perhaps you should iterate to "it != map.end() && it->first == 'a'" if you want all the a's.