Strange iterator behaviour - c++

#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.

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

Decrementing std::vector::iterator before std::vector::begin()

Following one of the "deleting while iterating" patterns on a vector, I don't understand why this code works, or if it's making use of undefined behavior:
The Code:
#include <vector>
#include <iostream>
int main(int argc, char* argv[], char* envz[])
{
std::vector<std::string> myVec;
myVec.push_back("1");
myVec.push_back("2");
myVec.push_back("3");
for (std::vector<std::string>::iterator i = myVec.begin();
i != myVec.end();
++i)
{
if ("1" == *i)
{
std::cout << "Erasing " << *i << std::endl;
i = myVec.erase(i);
--i;
continue;
}
std::cout << *i << std::endl;
}
return 0;
}
The Output:
>g++ -g main.cpp
>./a.out
Erasing 1
2
3
Question:
Consider the first iteration of the for-loop:
i is myVec.begin(), which "points to" 1.
We enter the conditional block.
1 is erased and i is set to one past the erased element, i.e. 2, which is now also pointed to by myVec.begin()
I decrement i, so now it points to...one prior to myVec.begin() ???
I'm confused by why this seems to work, as evidenced by the output, but something feels fishy about decrementing the iterator. This code is easy enough to rationalize if the conditional is if ("2" == *i), because the iterator decrement still places it at a valid entry in the vector. I.e. if we conditionally erased 2, i would be set to point to 3, but then manually decremented and thus point to 1, followed by the for-loop increment, setting it to point back to 3 again. Conditionally erasing the last element is likewise easy to follow.
What Else I Tried:
This observation made me hypothesize that decrementing prior to vector::begin() was idempotent, so I tried addition an additional decrement, like so:
#include <vector>
#include <iostream>
int main(int argc, char* argv[], char* envz[])
{
std::vector<std::string> myVec;
myVec.push_back("1");
myVec.push_back("2");
myVec.push_back("3");
for (std::vector<std::string>::iterator i = myVec.begin();
i != myVec.end();
++i)
{
if ("1" == *i)
{
std::cout << "Erasing " << *i << std::endl;
i = myVec.erase(i);
--i;
--i; /*** I thought this would be idempotent ***/
continue;
}
std::cout << *i << std::endl;
}
return 0;
}
But this resulted in a segfault:
Erasing 1
Segmentation fault (core dumped)
Can someone explain why the first code bock works, and specifically why the single decrement after erasing the first element is valid?
No, your code has undefined behaviour: if i == myVec.begin(), then i = myVec.erase(i); results in i again being (the new value of) myVec.begin(), and --i has undefined behaviour since it goes outside the valid range for the iterator.
If you don't want to use the erase-remove idiom (i.e. myVec.erase(std::remove(myVec.begin(), myVec.end(), "1"), myVec.end())), then the manual loop-while-mutating looks like this:
for (auto it = myVec.begin(); it != myVec.end(); /* no increment! */) {
if (*it == "1") {
it = myVec.erase(it);
} else {
++it;
}
}
Regardless, the crucial point both here and in your original code is that erase invalidates iterators, and thus the iterator must be re-assigned with a valid value after the erasing. We achieve this thanks to the return value of erase, which is precisely that new, valid iterator that we need.
This might work in some compilers, but might fail in others (e.g. the compiler might actually check in runtime that you are not decrementing under begin() and throw exception in such case - I believe that at least one compiler does it but don't remember which one).
In this case the general pattern is to not increment in the for but inside the loop:
for (std::vector<std::string>::iterator i = myVec.begin();
i != myVec.end();
/* no increment here */)
{
if ("1" == *i)
{
std::cout << "Erasing " << *i << std::endl;
i = myVec.erase(i);
continue;
}
std::cout << *i << std::endl;
++i;
}
With vector the wrong iteration might actually work in more cases, but you'd have very bad time if you try that e.g. with std::map or std::set.
The key here is the continue right after decrementing.
By calling it, ++i will be triggered by the loop iteration before dereferencing i.

Error on `it-1` with set iterator

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

Checking every element of vector with functor

I'm trying to detect whether every single element of the vector is fullfilling given condition, let's say it must even number.
#include <iostream>
#include <vector>
#include <algorithm>
bool isOdd(int i)
{
return i%2==0;
}
int main()
{
int arr[5]={1,2,3,4,5};
std::vector<int> myVec(arr, arr + sizeof(arr)/sizeof(arr[0]));
std::vector<int>::iterator it = std::find_if(myVec.begin(), myVec.end(),
isOdd());
// This piece of code is probably causing some problems;
while(myVec.empty()!=false) // while my vector IS NOT EMPTY
{
std::cout << *it << " "; // print out the value of elements that
// fullfiled the condition given in isOdd
}
return 0;
}
What is wrong with my way of thinking ? Is the condition in while loop wrong or maybe I've completely missed the logic ?
Can you please provide me with some complex explanation of what is wrong with this piece of code ?
Thank you in advance.
P.S. I know that there is a possibility to use lambda function instead, but I don't want to get too confused :)
The problem with your approach is that you are finding the odd number only once, and then for some reason you expect the vector to change, without making any modifications.
You should make a loop that calls find_if repeatedly, like this:
bool isOdd(int i) {
return i%2!=0;
}
...
std::vector<int>::iterator it = myVec.begin();
for (;;) {
it = std::find_if(it, myVec.end(), isOdd);
if (it == myVec.end()) {
break;
}
std::cout << *it << " ";
++it;
}
Demo.
Note: I changed your isOdd function to return true for odd numbers. The original version was returning true for even numbers.
find_if returns the iterator pointing to the first value which meets the given condition. It stops there. You can put this in a loop to find all such elements, until it returns the end iterator.
The following line does the exact opposite of what you meant:
while(myVec.empty()!=false) // while my vector IS NOT EMPTY
Either write
while(myVec.empty()==false)
or
while(myVec.empty()!=true)
or simpler
while(!myVec.empty())
You could write it as a for-loop:
for (auto it = find_if(begin(myVec), end(myVec), isOdd);
it != end(myVec);
it = find_if(it, end(myVec), isOdd))
{
// do something with "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));
}
}