Error on `it-1` with set iterator - c++

In following code I can not recognize difference between --it, and it-1.
Also is words.end() value is out of set range?
#include <iostream>
#include<set>
using namespace std;
int main()
{
set<string> words;
words.insert("test");
words.insert("crack");
words.insert("zluffy");
set<string>::iterator it=words.end();
cout<<*(--it)<<endl;//just works fine
cout<<*(it-1)<<endl;//Error
return 0;
}

That's because std::set::iterator is a constant BidrectionalIterator. That means it supports ++it and --it. However, it is not a RandomAccessIterator (like std::vector::iterator) - which is what it would need to be to support it + n and it - n.
While --it and it -= 1 conceputally do the same thing, the latter requires more functionality than std::set provides. That's why it's not supported. You could always use something like: std::prev(it, 1), which would work on any iterator that satisfies the BidirectionalIterator concept.

words.end() gives an iterator to an "element" past the end of the set. It does not point to anything so you cannot dereference it.
It serves at a bound for your set. You can for example scan your set from words.end() to words.begin() :
for (set<string>::iterator it = words.begin() ; it != words.end() ; ++it)
{
cout << *it << endl;
}

Related

Difference between using ++ and +1 when use iterator

We all know if we want to increase iterator we can use operator "++" or use "+1". Is there have difference betwen ++ and +1?
Now, I run this program:
#include <iostream>
#include <string>
#include <iterator>
using namespace std;
int Str(string &s, string::iterator it)
{
if(it == s.end())
return 0;
else
{
return (1 + Str(s,it++)); // Wrong line of code.When using "it + 1"
//programme could get right result but
//when using "it++" programme could not
// get right result
}
}
int main(int argc, char* argv[])
{
string s;
cout << "enter: ";
cin >> s;
string::iterator it = s.begin();
int i = Str(s,it);
cout << "str= " << i << endl;
return 0;
}
I use iterator as a function parameter.
Not all iterators can do +1. Only random access iterators can do this. All iterators can do ++ though. But there are two versions of ++.
Then your mistake is that i++ computes i+1 but returns the previous value. What you want is ++i, returning the new incremented iterator.
The issue is that in
return (1 + Str(s, it++));
you are using the postfix increment operator, which performs the increment but returns the iterator in its state before the increment. You can change this to
return (1 + Str(s, ++it));
which should yield the desired result. Note that it + 1 works for random access iterators only. It does the same in your case, but you could try to enforce as little constraints as possible for the iterators in use, e.g., when you change your container to one that works with bidirectional iterators, it would be desirable that your code still works. I'd hence recommend going with ++it.
Note that there is also the function template std::next, which increments the given iterator. It is also quite readable to go with
return (1 + Str(s, std::next(it));

Iterator invalidation - does end() count as an iterator or not?

I ran into the following problem using std::multimap::equal_range() and insert().
According to both cplusplus.com and cppreference.com, std::multimap::insert does not invalidate any iterators, and yet the following code causes an infinite loop:
#include <iostream>
#include <map>
#include <string>
int main(int argc, char* argv[])
{
std::multimap<std::string,int> testMap;
testMap.insert(std::pair<std::string,int>("a", 1));
testMap.insert(std::pair<std::string,int>("a", 2));
testMap.insert(std::pair<std::string,int>("a", 3));
auto range = testMap.equal_range(std::string("a"));
for (auto it = range.first; it != range.second; ++it)
{
testMap.insert(std::pair<std::string,int>("b", it->second));
// this loop becomes infinite
}
// never gets here
for (auto it = testMap.begin(); it != testMap.end(); ++it)
{
std::cout << it->first << " - " << it->second << std::endl;
}
return 0;
}
The intent is to take all existing items in the multimap with a particular key ("a" in this case) and duplicate them under a second key ("b"). In practice, what happens is that the first loop never exits, because it never ends up matching range.second. After the third element in the map is processed, ++it leaves the iterator pointing at the first of the newly inserted items.
I've tried this with VS2012, Clang, and GCC and the same thing seems to happen in all compilers, so I assume it's "correct". Am I reading too much into the statement "No iterators or references are invalidated."? Does end() not count as an iterator in this case?
multimap::equal_range returns a pair whose second element in this case is an iterator to the past-the-end element ("which is the past-the-end value for the container" [container.requirements.general]/6).
I'll rewrite the code a bit to point something out:
auto iBeg = testMap.begin();
auto iEnd = testMap.end();
for(auto i = iBeg; i != iEnd; ++i)
{
testMap.insert( std::make_pair("b", i->second) );
}
Here, iEnd contains a past-the-end iterator. The call to multimap::insert doesn't invalidate this iterator; it stays a valid past-the-end iterator. Therefore the loop is equivalent to:
for(auto i = iBeg; i != testMap.end(); ++i)
Which is of course an infinite loop if you keep adding elements.
The end-iterator range.second is not invalidated.
The reason that the loop is infinite, is that each repetition of the loop body:
inserts a new element at the end of the map, thus increasing the distance between it and the end by one (so, after this insert, range no longer represents the equal_range for the key "a" because you have inserted a new key within the range it does represent, from the first "a" to the end of the container).
increments it, reducing the distance between it and the end by one.
Hence, it never reaches the end.
Here's how I might write the loop you want:
for (auto it = testMap.lower_bound("a"); it != testMap.end() && it->first == "a"; ++it)
{
testMap.insert(std::pair<std::string,int>("b", it->second));
}
A solution to make it work as expected (feel free to improve, it's a community wiki)
auto range = testMap.equal_range(std::string("a"));
if(range.first != range.second)
{
--range.second;
for (auto it = range.first; it != std::next(range.second); ++it)
{
testMap.insert(std::pair<std::string,int>("b", it->second));
}
}

How to search for an element in an stl list?

Is there a find() function for list as there was in vector?
Is there a way to do that in list?
You use std::find from <algorithm>, which works equally well for std::list and std::vector. std::vector does not have its own search/find function.
#include <list>
#include <algorithm>
int main()
{
std::list<int> ilist;
ilist.push_back(1);
ilist.push_back(2);
ilist.push_back(3);
std::list<int>::iterator findIter = std::find(ilist.begin(), ilist.end(), 1);
}
Note that this works for built-in types like int as well as standard library types like std::string by default because they have operator== provided for them. If you are using using std::find on a container of a user-defined type, you should overload operator== to allow std::find to work properly - see EqualityComparable concept.
No, not directly in the std::list template itself. You can however use std::find algorithm like that:
std::list<int> my_list;
//...
int some_value = 12;
std::list<int>::iterator iter = std::find (my_list.begin(), my_list.end(), some_value);
// now variable iter either represents valid iterator pointing to the found element,
// or it will be equal to my_list.end()
Besides using std::find (from algorithm), you can also use std::find_if (which is, IMO, better than std::find), or other find algorithm from this list
#include <list>
#include <algorithm>
#include <iostream>
int main()
{
std::list<int> myList{ 5, 19, 34, 3, 33 };
auto it = std::find_if( std::begin( myList ),
std::end( myList ),
[&]( const int v ){ return 0 == ( v % 17 ); } );
if ( myList.end() == it )
{
std::cout << "item not found" << std::endl;
}
else
{
const int pos = std::distance( myList.begin(), it ) + 1;
std::cout << "item divisible by 17 found at position " << pos << std::endl;
}
}
What you can do and what you should do are different matters.
If the list is very short, or you are only ever going to call find once then use the linear approach above.
However linear-search is one of the biggest evils I find in slow code, and consider using an ordered collection (set or multiset if you allow duplicates). If you need to keep a list for other reasons eg using an LRU technique or you need to maintain the insertion order or some other order, create an index for it. You can actually do that using a std::set of the list iterators (or multiset) although you need to maintain this any time your list is modified.
No, find() method is not a member of std::list.
Instead, use std::find from <algorithm>
std :: list < int > l;
std :: list < int > :: iterator pos;
l.push_back(1);
l.push_back(2);
l.push_back(3);
l.push_back(4);
l.push_back(5);
l.push_back(6);
int elem = 3;
pos = find(l.begin() , l.end() , elem);
if(pos != l.end() )
std :: cout << "Element is present. "<<std :: endl;
else
std :: cout << "Element is not present. "<<std :: endl;

Strange iterator behaviour

#include "stdafx.h"
int _tmain(int argc, _TCHAR* argv[])
{
string s = "Haven't got an idea why.";
auto beg = s.begin();
auto end = s.end();
while (beg < end)
{
cout << *beg << '\n';
if (*beg == 'a')
{//whithout if construct it works perfectly
beg = s.erase(beg);
}
++beg;
}
return 0;
}
Why if I erase one or more chars from this string this code breaks? I suppose it has something to do with returned iterator after erase operation being created at higher address than end iterator but I'm not sure and it surely isn't right behaviour. Or is it?
There are several problems with this code.
Don't cache the value of s.end(); it changes as you delete elements.
Don't use beg < end. The idiomatic approach is to write beg != end. If you try to iterate past end, the result is undefined, and a debug version of the string library may deliberately crash your process, so it is meaningless to use <.
The iterator returned from s.erase(beg) might be s.end(), in which case ++beg takes you past the end.
Here's a (I think) correct version:
int _tmain(int argc, _TCHAR* argv[])
{
string s = "Haven't got an idea why.";
for (auto beg = s.begin(); beg != s.end();)
{
cout << *beg << '\n';
if (*beg == 'a')
{//whithout if construct it works perfectly
beg = s.erase(beg);
}
else
{
++beg;
}
}
}
EDIT: I suggest accepting FredOverflow's answer. It is simpler and faster than the above.
Erasing elements one by one from vectors or strings has quadratic complexity. There are better solutions with linear complexity:
#include <string>
#include <algorithm>
int main()
{
std::string s = "Haven't got an idea why.";
s.erase(std::remove(s.begin(), s.end(), 'a'), s.end());
std::cout << s << std::endl;
}
The previous s.end() value stored in end is invalid after s.erase(). Hence, do not use it.
Note the semantics of a basic_string and it's iterators.
From www.ski.com/tech/stl
Note also that, according to the C++ standard, basic_string has very unusual iterator invalidation semantics. Iterators may be invalidated by swap, reserve, insert, and erase (and by functions that are equivalent to insert and/or erase, such as clear, resize, append, and replace). Additionally, however, the first call to any non-const member function, including the non-const version of begin() or operator[], may invalidate iterators. (The intent of these iterator invalidation rules is to give implementors greater freedom in implementation techniques.)
Also what happens if
beg = s.erase(beg);
Returns an iterator equivalent to end()
On calling erase operation, stored end iterator pointer becomes invalid. So, use s.end() function in while loop condition
You have to iterate from .end()-1 to .begin(). At the same time, it is not safe to use comparison operators other than == and !=.
Here's my code:
vector<long long> myVector (my, my+myCount);
//sort and iterate through top correlation data counts
sort (myVector.begin(), myVector.end());
cout << endl;
int TopCorrelationDataCount = 0;
bool myVectorIterator_lastItem = false;
vector<long long>::iterator myVectorIterator=myVector.end()-1;
while (true) {
long long storedData = *myVectorIterator;
cout << TopCorrelationDataCount << " " << storedData << endl;
//prepare for next item
TopCorrelationDataCount++;
//if (TopCorrelationDataCount >= this->TopCorrelationDataSize) break;
if (myVectorIterator_lastItem) break;
myVectorIterator--;
if (myVectorIterator==myVector.begin())
{
myVectorIterator_lastItem = true;
}
}
Note: It can't be done using an ordinary for, because you have to find out if ==.begin(). If it is, this will be your last iteration. You can't check if ==.begin()-1, as it will result in run time error.
If you only want to use to X items in a vector, use TopCorrelationDataCount.

List Iterator Remove()

I have a list iterator that goes through a list and removes all the even numbers. I can use the list iterator to print out the numbers fine but I cannot use the list's remove() and pass in the dereferenced iterator.
I noticed that when the remove() statement is in effect, *itr gets corrupted? Can somebody explain this?
#include <iostream>
#include <list>
#define MAX 100
using namespace std;
int main()
{
list<int> listA;
list<int>::iterator itr;
//create list of 0 to 100
for(int i=0; i<=MAX; i++)
listA.push_back(i);
//remove even numbers
for(itr = listA.begin(); itr != listA.end(); ++itr)
{
if ( *itr % 2 == 0 )
{
cout << *itr << endl;
listA.remove(*itr); //comment this line out and it will print properly
}
}
}
There are a few issues with your code above. Firstly, the remove will invalidate any iterators that are pointing at the removed elements. You then go on to continue using the iterator. It is difficult to tell which element(s) remove would erase in the general case (although not in yours) since it can remove more than one.
Secondly, you are probably using the wrong method. Remove will iterate through all of the items in the list looking for any matching elements - this will be inefficient in your case because there is only one. It looks like you should use the erase method, you probably only want to erase the item at the position of the iterator. The good thing about erase is it returns an iterator which is at the next valid position. The idiomatic way to use it is something like this:
//remove even numbers
for(itr = listA.begin(); itr != listA.end();)
{
if ( *itr % 2 == 0 )
{
cout << *itr << endl;
itr=listA.erase(itr);
}
else
++itr;
}
Finally, you could also use remove_if to do the same as you are doing:
bool even(int i) { return i % 2 == 0; }
listA.remove_if(even);
You can't use an iterator after you delete the element it referred to.
However, list iterators which refer to non-deleted items after a remove() should remain valid.
Could we use something like this:
container.erase(it++);
I tried on this example:
int main(){
list<int>*a=new list<int>;
a->push_back(1);
a->push_back(2);
a->push_back(3);
list<int>::iterator I;
I=a->begin(); ++I;
a->erase(I++);
cout<<*I<<endl;
}
and it displayed 3, as I wanted. Now I don't know if this is valid or one of those which "sometimes work and sometimes not".
EDIT: Maybe it is because of compiler. For example, compiler I am using (GNU gcc-g++) is treating lists (std::) as circular, ie if I increase iterator after list->end() it puts you at the beginning.
Since iterators depend on the length of the structure remaining the same, most iterators do not allow a list to be changed while the iterator is in use. If you want to go through and change the list, you're going to have to use a loop independent of the iterator.