I'm trying to understand iterators and different ways to iterate over elements in different containers. What's the difference between these two options?
for(auto it = myArray.begin(); it < myArray.end(); it++){
std::cout << *it << std::endl;
}
for(auto it = myArray.begin(); it != myArray.end(); it++){
std::cout << *it << std::endl;
}
When using the second option my editor suggests using a range based for loop in stead, which made me realise I don't know the difference.
for(int & it : myArray){
std::cout << it << std::endl;
}
What's the difference between these two options?
Not all types of input iterators are comparable using the < operator (only random-access iterators are, but not all iterators are random-access). All input iterators are comparable using the == and/or != operator, though.
When using the second option my editor suggests using a range based for loop instead
As you should. A range-for loop uses iterators internally, similar to your second for loop. The loop variable is the dereferenced value, not the iterator itself. A range-for loop does not expose access to its internal iterators.
Related
This question already has answers here:
Iterating C++ vector from the end to the beginning
(13 answers)
Closed 2 years ago.
Suppose I'm a newbie C++ programmer. I have a C++ container; say, a vector:
std::vector<int> vec { 12, 34, 56, 78 };
I know I can iterate over all of the elements with a simple loop:
for(std::vector<int>::size_type i = 0; i < vec.size(); i++) {
std::cout << vec[i] << '\n';
}
and maybe I've even learned a little about Modern C++, so I know I can use a ranged-for loop:
for(auto x : vec) {
std::cout << x << '\n';
}
But now, I want to iterate over the elements in reverse order. The range-based for loop won't work as such. With a plain loop, I have to be careful and avoid underflow, so perhaps something like this? :
for(std::vector<int>::size_type i = 0; i < vec.size(); i++) {
std::cout << vec[vec.size() - i] << '\n';
}
but - I don't like having the loop counter mean the opposite of what we're looking at. But if I started i at vec.size()-1, I would risk underflow after the last element. So I would need to do this, maybe?
for(std::vector<int>::size_type i = vec.size(); i > 0 ; i--) {
std::cout << vec[i - 1] << '\n';
}
well, that doesn't feel right either. What idioms should I use for reverse iteration, which are safe (i.e. difficult to get wrong) , aesthetically pleasing and reasonable terse?
Notes:
I tried to phrase the title to be as simple as possible (rather than saying "reverse-iterate a container").
Motivated by this question, where a naive reverse-iteration loop has a bug.
I do not want to make a copy of the container with the elements and reverse and iterate over that the usual way.
I didn't use auto& or const auto& in the loops above since newbie coders often don't know about them.
Well, first of all, about your two snippets: Part of the problem is that they're a bit bug prone for actual newbies - the integer underflow, off-by-one in the comparison, forgetting what i signifies and using it as a plain index etc. So I would definitely recommend something else. Also, those snippets may invoke vec.size() many times, which, if the compiler isn't optimizing well enough, would mean a bunch of redundant work.
Option 1: Use iterators
You can reverse-iterate over a container using a pair of iterators (std::rbegin and std::rend, and their constant variants) which represent the reversal of the container's order of elements. Here's what that looks like:
for(auto it = std::crbegin(vec); it != std::crend(vec); it++) {
std::cout << *it << '\n';
}
I made this option the first because it's (mostly) compatible with C++98. We didn't have std::rbegin() and std::crbegin() then, but we did have an rbegin() method for std::vector. std::crbegin() was introduced in C++11
Option 2: Using C++11 (and later) ranged-for loops
You can massage your container - without making a copy of it (although possibly with some payment of time), so that you can use the result in ranger for loop. The answers to this SO question describe several ways to do so, enabling the following code:
auto reverse_view = /* magic involving vec; and not making a copy */
for(auto x : reverse_view) {
std::cout << *it << '\n';
}
They involve either using an "infrastructural" library (namely Boost), or writing a few lines of code which return an iterator pair in an std::pair - which is enough for C++ to use in a ranged-for loop.
Option 3: Using ranged-for and C++20's ranges support
Finally, in C++20, this all becomes easier - with ranges support and std::ranges::reverse_view:
auto reverse_view = std::ranges::reverse_view{vec};
for (const auto& x : reverse_view) {
std::cout << x << '\n';
}
Performance note
Reverse-iterating can in some cases be expensive - because moving backwards, or finding the end of the container, is not always trivial or free. Think of a unidirectional list (where each element comes with a pointer to the next one) - whenever you want to go backwards, you need to traverse the whole list up to your current element to know where the previous element is located. Not all containers are like vectors...
What is the difference between set::key_comp vs set::value_comp in C++? Going to cplusplus.com page there is no significant difference.
Furthermore on set::key_comp & related set::value_comp pages
last sentence is "(...) key_comp and its sibling member function value_comp are equivalent."
Examples are almost the same:
http://www.cplusplus.com/reference/set/set/key_comp/
http://www.cplusplus.com/reference/set/set/value_comp/
key_comp defines the order of the keys in a container.
value_comp defines the order of the values in a container.
In a std::set where, essentially, the values are the keys, the two are indeed exactly equivalent. But that's not true in all containers, e.g. std::map, or, in general, in a container that you might build yourself that follows the conventions of the C++ Standard Library Containers.
Note also that http://en.cppreference.com/w/ is a superior reference for C++. It pretty much proxies the standards.
These are identical, both must be made available by any implementation because std::set must meet the requirement of Associative Container.
This allows you to write generic code that works with any Associative Container (std::set, std::map, std::multiset, std::multimap in the standard library).
The difference comes when key and value are different entities inside a container.
For containers like set, these two terms mean same thing.
While, for containers like map or multimap, the key and value are separate entities maintained as an single entry.
Here is an example which shows how they differ:
std::set<int> myset;
int highest1, highest2, highest3;
typedef map<int, int> MyMap;
MyMap mymap;
std::set<int>::key_compare myCompKeyForSet = myset.key_comp();
std::set<int>::value_compare myCompValForSet = myset.value_comp();
MyMap::key_compare myCompKeyForMap = mymap.key_comp();
MyMap::value_compare myCompValForMap = mymap.value_comp();
for (int i=0; i<=5; i++) {
myset.insert(i);
mymap.insert(make_pair(i, 2*i));
}
//////SET///////
highest1=*myset.rbegin();
std::set<int>::iterator it=myset.begin();
while ( myCompKeyForSet(*it, highest1) ) it++;
std::cout << "\nhighest1 is " << highest1; // prints 5
highest2 = *myset.rbegin();
it=myset.begin();
while ( myCompValForSet(*it, highest2) ) it++;
std::cout << "\nhighest2 is " << highest2; // prints 5
//////MAP///////
MyMap::iterator it2 = mymap.begin();
highest3 = mymap.rbegin()->first;
while ( myCompKeyForMap((it2->first), highest3) ) it2++;
std::cout << "\nhighest3 is " << highest3; // prints 5
std::pair<int,int> highest4 = *mymap.rbegin(); //must be defined as map's `value_type`
it2 = mymap.begin();
while ( myCompValForMap(*(it2), highest4) ) it2++; // takes `value_type` which is `pair<int, int>` in this case.
std::cout << "\nhighest4 is " << highest4.second; // prints 10
Live demo
As I mentioned the passed arguments to value_compare function object must be of type value_type&, so I am in a kind of disagreement with those saying that these two set::key_comp and set::value_comp are easily compatible across associative containers.
Suppose, I have declared a vector in C++ like this:
vector<int>numbers = {4,5,3,2,5,42};
I can iterate it through the following code:
for (vector<int>::iterator it = numbers.begin(); it!=numbers.end(); it++){
// code goes here
}
Now, I would talk about coding in the block of for loop.
I can access and change any value using this iterator. say, I want to increase every value by 10 and the print. So, the code would be:
*it+=10;
cout << *it << endl;
I can print the address of both iterator and elements that are being iterated.
Address of iterator can be printed by:
cout << &it << endl;
Address of iterated elements can be printed by:
cout << &(*it) << endl;
But why the iterator itself could not printed by doing the following?
cout << it <<endl;
At first I thought the convention came from JAVA considering the security purpose. But if it is, then why I could print it's address?
However, Is there any other way to do this? If not, why?
Yes, there is a way to do it!
You can't print the iterator because it is not defined to have a value.
But you can perform arithematic operations on them and that helps you to print the value (of the iterator).
Do the following.
cout << it - v.begin();
Example:
#include <iostream>
#include <algorithm>
#include <vector>
#include <iterator>
using namespace std;
int main () {
vector<int> v = {20,3,98,34,20,11,101,201};
sort (v.begin(), v.end());
vector<int>::iterator low,up;
low = lower_bound (v.begin(), v.end(), 20);
up = upper_bound (v.begin(), v.end(), 20);
std::cout << "lower_bound at position " << (low - v.begin()) << std::endl;
std::cout << "upper_bound at position " << (up - v.begin()) << std::endl;
return 0;
}
Output of the above code:
lower_bound at position 2
upper_bound at position 4
Note: this is just a way to get things done and no way I have claimed that we can print the iterator.
...
There is no predefined output operator for the standard iterators because there is no conventional meaning of printing an iterator. What would you expect such an operation to print? While you seem to expect to see the address of the object the iterator refers to, I find that not clear at all.
There is no universal answer to that, so the committee decided not to add a those operators. (The last half sentence is a guess, I am not part of the committee.)
If you want to print those iterators, I would define a function like print(Iterator); (or something like this, whatever fits your needs) that does what you want. I would not add an operator << for iterators for the reason I mentioned above.
why the iterator itself could not printed by doing the following?
Because, it is not defined to a value internally.
Is there any other way to do this?
Basically, the compiler does not facilitate it by default, you may try to edit the compiler code! But it is too terrific you know!
If not, why?
Because it has no well-defined way to express it.
You can't print the iterator because it is not defined to have a value. But you can perform arithematic operations on them and that helps you to print the value (of the iterator).
I want to construct nested loops over arrays of objects, having a rather complex data structure. Because I use arrays, I want to make use of their iterators. After I got unexpected results I boiled down the problem to the following code snippet, that shows my iterators to be equal when I expect them to be different:
vector<int> intVecA;
vector<int> intVecB;
intVecA.push_back(1);
intVecA.push_back(2);
intVecB.push_back(5);
intVecB.push_back(4);
Foo fooOne(intVecA);
Foo fooTwo(intVecB);
vector<int>::const_iterator itA = fooOne.getMyIntVec().begin();
vector<int>::const_iterator itB = fooTwo.getMyIntVec().begin();
cout << "The beginnings of the vectors are different: "
<< (fooOne.getMyIntVec().begin() == fooTwo.getMyIntVec().begin()) << endl;
cout << (*(fooOne.getMyIntVec().begin()) == *(fooTwo.getMyIntVec().begin())) << endl;
cout << (&(*(fooOne.getMyIntVec().begin())) == &(*(fooTwo.getMyIntVec().begin()))) << endl;
cout << "But the iterators are equal: "
<< (itA==itB) << endl;
This produces:
The beginnings of the vectors are different: 0
0
0
But the iterators are equal: 1
This behaviour does not make sense to me and I'd be happy about hearing an explanation.
Foo is a simple object containing a vector and getter function for it:
class Foo {
public:
Foo(std::vector<int> myIntVec);
std::vector<int> getMyIntVec() const {
return _myIntVec;
}
private:
std::vector<int> _myIntVec;
};
Foo::Foo(std::vector<int> myIntVec) {
_myIntVec = myIntVec;
}
When first copying the vectors the problem vanishes. Why?
vector<int> intVecReceiveA = fooOne.getMyIntVec();
vector<int> intVecReceiveB = fooTwo.getMyIntVec();
vector<int>::const_iterator newItA = intVecReceiveA.begin();
vector<int>::const_iterator newItB = intVecReceiveB.begin();
cout << "The beginnings of the vectors are different: "
<< (intVecReceiveA.begin() == intVecReceiveB.begin()) << endl;
cout << "And now also the iterators are different: "
<< (newItA==newItB) << endl;
produces:
The beginnings of the vectors are different: 0
And now also the iterators are different: 0
Further notes:
I need these nested loops in functions which need to be extremely efficient regarding computation time, thus I would not want to do unnecessary operations. Since I'm new to c++ I do not know whether copying the vectors would actually take additional time or whether they would be copied internally anyway. I'm also thankful for any other advice.
The problem is that your accessor in Foo:
std::vector<int> getMyIntVec() const {
return _myIntVec;
}
I doesn't return _myIntVec, it returns a copy of myIntVec.
Instead it should look like:
const std::vector<int>& getMyIntVec() const {
return _myIntVec;
}
Otherwise when you create iterators they are created from copies that are directly thrown away so your C++ compiler reuses the address. That is why you get "equal" iterators, at least I think so.
You realize that you compare things the wrong way round? If you compare a == b, even if you write
cout << "a is different from b: " << (a==b) << endl;
The output will tell if the two elements are the same not different. To check if two things are different use != instead of ==.
The reason for this is that it is undefined behaviour to compare two iterators which refer to elements in different containers. So, there is no guarantee what you will get. This comes from the fact that getMyIntVec returns a copy of _MyIntVec and you assign these copies to new instances of vector<int>, so these are indeed iterators of two different copies of the _MyIntVec member.
According to the standard:
§ 24.2.1
An iterator j is called reachable from an iterator i if and only if there is a finite sequence of applications of
the expression ++i that makes i == j. If j is reachable from i, they refer to elements of the same sequence.
and a bit later in the standard:
§ 24.2.5
The domain of == for forward iterators is that of iterators over the same underlying sequence.
This has already been answered in this question
You have a serious logic problem here:
cout << "The beginnings of the vectors are different: "
<< (fooOne.getMyIntVec().begin() == fooTwo.getMyIntVec().begin()) << endl;
If they are equal, it will output 1 instead of 0 which you normally expect.
I am new to the C++ language. I have been starting to use vectors, and have noticed that in all of the code I see to iterate though a vector via indices, the first parameter of the for loop is always something based on the vector. In Java I might do something like this with an ArrayList:
for(int i=0; i < vector.size(); i++){
vector[i].doSomething();
}
Is there a reason I don't see this in C++? Is it bad practice?
The reason why you don't see such practice is quite subjective and cannot have a definite answer, because I have seen many of the code which uses your mentioned way rather than iterator style code.
Following can be reasons of people not considering vector.size() way of looping:
Being paranoid about calling size() every time in the loop
condition. However either it's a non-issue or it can be trivially
fixed
Preferring std::for_each() over the for loop itself
Later changing the container from std::vector to other one (e.g.
map, list) will also demand the change of the looping mechanism,
because not every container support size() style of looping
C++11 provides a good facility to move through the containers. That is called "range based for loop" (or "enhanced for loop" in Java).
With little code you can traverse through the full (mandatory!) std::vector:
vector<int> vi;
...
for(int i : vi)
cout << "i = " << i << endl;
The cleanest way of iterating through a vector is via iterators:
for (auto it = begin (vector); it != end (vector); ++it) {
it->doSomething ();
}
or (equivalent to the above)
for (auto & element : vector) {
element.doSomething ();
}
Prior to C++0x, you have to replace auto by the iterator type and use member functions instead of global functions begin and end.
This probably is what you have seen. Compared to the approach you mention, the advantage is that you do not heavily depend on the type of vector. If you change vector to a different "collection-type" class, your code will probably still work. You can, however, do something similar in Java as well. There is not much difference conceptually; C++, however, uses templates to implement this (as compared to generics in Java); hence the approach will work for all types for which begin and end functions are defined, even for non-class types such as static arrays. See here: How does the range-based for work for plain arrays?
Is there any reason I don't see this in C++? Is it bad practice?
No. It is not a bad practice, but the following approach renders your code certain flexibility.
Usually, pre-C++11 the code for iterating over container elements uses iterators, something like:
std::vector<int>::iterator it = vector.begin();
This is because it makes the code more flexible.
All standard library containers support and provide iterators. If at a later point of development you need to switch to another container, then this code does not need to be changed.
Note: Writing code which works with every possible standard library container is not as easy as it might seem to be.
The right way to do that is:
for(std::vector<T>::iterator it = v.begin(); it != v.end(); ++it) {
it->doSomething();
}
Where T is the type of the class inside the vector. For example if the class was CActivity, just write CActivity instead of T.
This type of method will work on every STL (Not only vectors, which is a bit better).
If you still want to use indexes, the way is:
for(std::vector<T>::size_type i = 0; i != v.size(); i++) {
v[i].doSomething();
}
Using the auto operator really makes it easy to use as one does not have to worry about the data type and the size of the vector or any other data structure
Iterating vector using auto and for loop
vector<int> vec = {1,2,3,4,5}
for(auto itr : vec)
cout << itr << " ";
Output:
1 2 3 4 5
You can also use this method to iterate sets and list. Using auto automatically detects the data type used in the template and lets you use it.
So, even if we had a vector of string or char the same syntax will work just fine
A correct way of iterating over the vector and printing its values is as follows:
#include<vector>
// declare the vector of type int
vector<int> v;
// insert elements in the vector
for (unsigned int i = 0; i < 5; ++i){
v.push_back(i);
}
// print those elements
for (auto it = v.begin(); it != v.end(); ++it){
std::cout << *it << std::endl;
}
But at least in the present case it is nicer to use a range-based for loop:
for (auto x: v) std::cout << x << "\n";
(You may also add & after auto to make x a reference to the elements rather than a copy of them. It is then very similar to the above iterator-based approach, but easier to read and write.)
There's a couple of strong reasons to use iterators, some of which are mentioned here:
Switching containers later doesn't invalidate your code.
i.e., if you go from a std::vector to a std::list, or std::set, you can't use numerical indices to get at your contained value. Using an iterator is still valid.
Runtime catching of invalid iteration
If you modify your container in the middle of your loop, the next time you use your iterator it will throw an invalid iterator exception.
Here is a simpler way to iterate and print values in vector.
for(int x: A) // for integer x in vector A
cout<< x <<" ";
With STL, programmers use iterators for traversing through containers, since iterator is an abstract concept, implemented in all standard containers. For example, std::list has no operator [] at all.
I was surprised nobody mentioned that iterating through an array with an integer index makes it easy for you to write faulty code by subscripting an array with the wrong index. For example, if you have nested loops using i and j as indices, you might incorrectly subscript an array with j rather than i and thus introduce a fault into the program.
In contrast, the other forms listed here, namely the range based for loop, and iterators, are a lot less error prone. The language's semantics and the compiler's type checking mechanism will prevent you from accidentally accessing an array using the wrong index.
don't forget examples with const correctness - can the loop modify the elements. Many examples here do not, and should use cont iterators. Here we assume
class T {
public:
T (double d) : _d { d } {}
void doSomething () const { cout << _d << endl; return; }
void changeSomething () { ++_d; return; }
private:
double _d;
};
vector<T> v;
// ...
for (const auto iter = v.cbegin(); iter != v.cend(); ++iter) {
iter->doSomething();
}
Note also, that with the C++11 notation, the default is to copy the element. Use a reference to avoid this, and/or to allow for original elements to be modified:
vector<T> v;
// ...
for (auto t : v) {
t.changeSomething(); // changes local t, but not element of v
t.doSomething();
}
for (auto& t : v) { // reference avoids copying element
t.changeSomething(); // changes element of v
t.doSomething();
}
for (const auto& t : v) { // reference avoids copying element
t.doSomething(); // element can not be changed
}
//different declaration type
vector<int>v;
vector<int>v2(5,30); //size is 5 and fill up with 30
vector<int>v3={10,20,30};
//From C++11 and onwards
for(auto itr:v2)
cout<<"\n"<<itr;
//(pre c++11)
for(auto itr=v3.begin(); itr !=v3.end(); itr++)
cout<<"\n"<<*itr;
int main()
{
int n;
int input;
vector<int> p1;
vector<int> ::iterator it;
cout << "Enter the number of elements you want to insert" << endl;
cin >> n;
for (int i = 0;i < n;i++)
{
cin >> input;
p1.push_back(input);
}
for(it=p1.begin();it!=p1.end();it++)
{
cout << *it << endl;
}
//Iterating in vector through iterator it
return 0;
}
conventional form of iterator
If you use
std::vector<std::reference_wrapper<std::string>> names{ };
Do not forget, when you use auto in the for loop, to use also get, like this:
for (auto element in : names)
{
element.get()//do something
}