Function with set<string> and iterator - c++

this is my homework:
Write a function to prints all strings with a length of 3. Your
solution must use a for loop with iterators.
void print3(const set & str)
And this is my code:
void print3(const set<string>& str){
string st;
set<string,less<string>>::iterator iter;
for(iter=str.begin();iter!=str.end();++iter)
{st=*iter;
if(st.length()==3) cout<<st<<' ';
}
}
But I think it's not good. Do someone have a better code? Please, help me to improve it.
-I have another question about iterator
string name[]={"halohg","nui","ght","jiunji"};
set<string> nameSet(name,name+4);
set<string>::iterator iter;
iter=name.begin();
How can I access name[2]="ght" by using iterator?
I tried iter+2 but it has some problems. I think I have to use random access iterator but I don't know how to use it.
Please, help me. Thanks a lot!

Some thoughts on improvement:
You can get rid of string st; and just check if (iter->length() == 3).
Another improvement would be to use a const_iterator instead of an iterator, since you aren't modifying any of the items.
Also, adding less<string> as a template parameter is kind of useless, since that's the default compare functor anyway, so it can be removed.
And lastly, it's generally a good idea to declare your locals with minimal scope (so they don't pollute other scopes or introduce unexpected hiding issues), so usually you want to declare your iter in the for.
So it becomes:
for (set<string>::const_iterator iter = str.begin(); iter != str.end(); ++iter) {
if (iter->length() == 3) cout << *iter << ' ';
}
That's about as good as you can get, given your requirements.
As for your second question, set's iterator is not a random access iterator. It's a (constant) Bidirectional Iterator. You can use std::advance if you wanted, though, and do:
std::set<std::string>::iterator iter;
iter = name.begin();
std::advance(iter, 2);
// iter is now pointing to the second element
Just remember that set sorts its elements.

Related

Iterating in reverse direction using rbegin() and begin()

When we are iterating in reverse direction, I see that most people use the following structure:
for (auto it = vec.rbegin(); it != vec.rend(); it++)
{
// block of code //
}
But for a long time, I have a doubt about using this, and I want to know why the following code does not work.
As we know, the last element will have the highest index than any element index in the array, and the array is going to take contiguous memory.
My primary doubt is when iterating backwards, why shouldn't we use it--?
I want the reason why the following code is not going to work. I am running the loop from rbegin, that is the last element, and I am going until the first element. I am decrementing it by one in every iteration.
for (auto it = vec.rbegin(); it >= vec.begin(); it--)
{
cout << *it << endl;
}
Even the below code is not working, why?
for(auto it = vec.rbegin(); it >= vec.begin(); it++)
{
cout << *it << endl;
}
First of all, in the given codes, the for loop's conditions are making issue due to type-mismatch.
The vec.rbegin() gives the std::vector::reverse_iterator, and the vec.begin() gives the std::vector::iterator; those are different types and can not be compared. Hence, you get compiler errors in those places.
When iterating backwards, why shouldn't we use it--?
See the following reference picture from std::reverse_iterator
When you use rbegin(), you start from the last element. In order to advance further (like every iterator implementation) it uses the operator++. Advance here means, iterating backwards direction, because the starting point is the last element. Therefore, you should be using it++ or ++it instead.
For the last for loop example, however, there is only a type-mismatch issue. Using ✱std::reverse_iterator::base(), you could get/ convert the reverse iterator to the corresponding base iterator, and it can be compared with the vec.begin().
That means the following change will make it work:
for (auto it = vec.rbegin(); it.base() != vec.begin(); ++it)
// ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
{
std::cout << *it << " ";
}
See a demo
Side Note:
Even though, the above is possible, I would strongly suggest use the same iterators for comparison, which provides the code more natural look, and easy to understand for the fellow devs and less error-prone.
✱Read more: Can I convert a reverse iterator to a forward iterator?
In all, it is just a design issue, the designer designed the begin, rbegin, end, rend in that way.
Take an example of a container with three elements {1,2,3}.
begin() points to 1, end() points to the position after 3
rbegin() points to 3, rend() points to the position before 1.
You can understand rbegin() as a special data struct of a special pointer (aka iterator) such that + operator would be overloaded into -.
You can but not recommended to mix rbegin() with begin() cause they are different things. And mixing is always error-prone for most of the time.
Reverse iterators are designed to mimic forward iterators (and iterators in general are designed to mimic pointers), so algorithms can be written in an agnostic way that works with both types. All iterators advance with operator++ and decrement with operator-- , where
forward iterators advance in a forward direction and decrement in a backwards direction
reverse iterators advance in a backward direction and decrement in a forward direction

Why we need rbegin and rend?

Since we now have advance() and the prev() to move iterator to go front or go back, and we already have begin() and end().
I wonder is there any situation we better/have to move reverse iterator back and front?
Algorithms often take two iterators that specify a range of elements. For example std::for_each:
std::vector<int> x;
std::for_each(x.begin(),x.end(),foo);
If you want to make for_each iterate in reverse order (note: for_each does iterate in order) then neither advance nor prev are of any help, but you can use reverse iterators:
std::for_each(x.rbegin(),x.rend(),foo);
Because using begin() and end() to iterate in reverse looks horrible:
std::vector<int> v {1, 2, 3};
if(!v.empty()) { //need to make sure of that before we decrement
for(auto it = std::prev(v.end()); ; --it) {
//do something with it
if(it == v.begin()) {
break;
}
}
}
Compare it with reverse iterator version:
std::vector<int> v {1, 2, 3};
for(auto it = v.rbegin(); it != v.rend(); it++) {
//do something with it
}
When you have a function template that takes iterators, and want it to operate on the data in reverse.
E.g.
std::string s = "Hello";
std::string r(s.rbegin(), s.rend());
std::cout << r;
When you use algorithms like std::for_each(), std::accumulate(), std::find_if()... they systematically progress with ++.
If you want this progression to physically occur backwards, then the reverse
iterators are useful.
I guess it is good practise because it seems odd if you start from end and finish in begin. You can easily say last but one by using rbegin.
vector::reverse_iterator itr1;
for (itr1 = vec.rbegin(); itr1 < vec.rend(); itr1++) {
if (*itr1 == num) {
vec.erase((itr1 + 1).base());
}
}
You can use as a function which deletes that Which num want to erase in vector
The need for rbegin()/rend() is because begin() is not the same as rend(), and end() is not rbegin(), see this image from cppreference
This way, you can use any algorithm going forward from beginning to end or backwards from the last to the first element.
There are examples with for each. However, more general, it allows you to reuse any algorithm or operators that works with iterators with advancing, to do the same thing but in a reverse order.

Incrementing using iterator

I just started using iterators a few days ago. And while I do get what the basic idea is, I need help clearing something up.
I'm learning c++ from a book and at the end of the chapter about iterator there's a little assignment that goes like this:
"Assuming that "scores" is a vector that holds elements of type int, what's wrong with the following code snippet (meant to increment each element)?
vector<int>::iterator iter;
for(iter =scores.begin(); iter !=scores.end(); iter++)
{
iter++;
}
now, it's obvious the iterator iter is already getting incremented by the for loop even without the code inside the loop. so I thought I should just do this do increment the values of the vector that the iterator was referencing.
for(iter =scores.begin(); iter !=scores.end(); iter++)
{
*iter++;
}
But that returned an error too. So it got me thinking... is it even possible to increment values using an iterator ? Or is an iterator just an object used to, well, iterate through objects in a container and reference them ? Without being able to actually modify their value ?
Change it to:
for(iter =scores.begin(); iter !=scores.end(); iter++)
{
(*iter)++;
}
In C++ operators have different precedences, see here for a table.
Postfix increment is performed before the de-reference.

Use a regular iterator to iterate backwards, or struggle with reverse_iterator?

I recently learned about the right way to work with reverse iterators in C++ (specifically when you need to erase one). (See this question and this one.)
This is how you're supposed to do it:
typedef std::vector<int> IV;
for (IV::reverse_iterator rit = iv.rbegin(), rend = iv.rend();
rit != rend; ++rit)
{
// Use 'rit' if a reverse_iterator is good enough, e.g.,
*rit += 10;
// Use (rit + 1).base() if you need a regular iterator e.g.,
iv.erase((rit + 1).base());
}
But I think thought this is much better (Don't do this, not standards compliant, as MooingDuck points out):
for (IV::iterator it = iv.end(), begin = iv.begin();
it-- != begin; )
{
// Use 'it' for anything you want
*it += 10;
iv.erase(it);
}
Cons:
You tell me. What's wrong with it?
It's not standards compliant, as MooingDuck points out. That pretty much overrules any of the possible advantages below.
Pros:
Uses a familiar idiom for reverse for-loops
Don't have to remember (or explain) the +1
Less typing
Works for std::list too: it = il.erase(it);
If you erase an element, you don't have to adjust the iterator
If you erase, you don't have to recompute the begin iterator
The reason for reverse iterators is that the standard algorithms do not know how to iterate over a collection backwards. For example:
#include <string>
#include <algorithm>
std::wstring foo(L"This is a test, with two letter a's involved.");
std::find(foo.begin(), foo.end(), L'a'); // Returns an iterator pointing
// to the first a character.
std::find(foo.rbegin(), foo.rend(), L'a').base()-1; //Returns an iterator
// pointing to the last A.
std::find(foo.end(), foo.begin(), L'a'); //WRONG!! (Buffer overrun)
Use whichever iterator results in clearer code.
For what it's worth, Scott Meyers' Effective STL recommends that you just stick with a regular ol' iterator (Item 26).

How do you iterate backwards through an STL list?

I'm writing some cross-platform code between Windows and Mac.
If list::end() "returns an iterator that addresses the location succeeding the last element in a list" and can be checked when traversing a list forward, what is the best way to traverse backwards?
This code workson the Mac but not on Windows (can't decrement beyond first element):
list<DVFGfxObj*>::iterator iter = m_Objs.end();
for (iter--; iter!=m_Objs.end(); iter--)// By accident discovered that the iterator is circular ?
{
}
this works on Windows:
list<DVFGfxObj*>::iterator iter = m_Objs.end();
do{
iter--;
} while (*iter != *m_Objs.begin());
Is there another way to traverse backward that could be implemented in a for loop?
Use reverse_iterator instead of iterator.
Use rbegin() & rend() instead of begin() & end().
Another possibility, if you like using the BOOST_FOREACH macro is to use the BOOST_REVERSE_FOREACH macro introduced in Boost 1.36.0.
The best/easiest way to reverse iterate a list is (as already stated) to use reverse iterators rbegin/rend.
However, I did want to mention that reverse iterators are implemented storing the "current" iterator position off-by-one (at least on the GNU implementation of the standard library).
This is done to simplify the implementation, in order for the range in reverse to have the same semantics as a range forward [begin, end) and [rbegin, rend)
What this means is that dereferencing an iterator involves creating a new temporary, and then decrementing it, each and every time:
reference
operator*() const
{
_Iterator __tmp = current;
return *--__tmp;
}
Thus, dereferencing a reverse_iterator is slower than an normal iterator.
However, You can instead use the regular bidirectional iterators to simulate reverse iteration yourself, avoiding this overhead:
for ( iterator current = end() ; current != begin() ; /* Do nothing */ )
{
--current; // Unfortunately, you now need this here
/* Do work */
cout << *current << endl;
}
Testing showed this solution to be ~5 times faster for each dereference used in the body of the loop.
Note: Testing was not done with the code above, as that std::cout would have been the bottleneck.
Also Note: the 'wall clock time' difference was ~5 seconds with a std::list size of 10 million elements. So, realistically, unless the size of your data is that large, just stick to rbegin() rend()!
You probably want the reverse iterators. From memory:
list<DVFGfxObj*>::reverse_iterator iter = m_Objs.rbegin();
for( ; iter != m_Objs.rend(); ++iter)
{
}
As already mentioned by Ferruccio, use reverse_iterator:
for (std::list<int>::reverse_iterator i = s.rbegin(); i != s.rend(); ++i)
This should work:
list<DVFGfxObj*>::reverse_iterator iter = m_Objs.rbegin();
for (; iter!= m_Objs.rend(); iter++)
{
}