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));
Related
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.
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;
}
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"
}
Code:
#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;
template<typename T, typename C> T pfind(T f, T e, C c){
do{
for (int i = 1; f + i != e; i++)
if (c(*f, *f + i))
return (f + i);
} while (++f != e);
}
int main()
{
vector<int>vstr = { 1, 2, 3, 4,4,5,5,5 };
vstr.erase(pfind(vstr.begin(), vstr.end(),equal_to<int>()));
for (auto&itr : vstr){
cout << itr << " ";
}
getchar();
}
This code crashes...
But when i tried to use less instead of equal_to its working....
Could anyone please explain me why?
If you'd paid attention to the compiler warnings, you'd have had a clue why your function fails. When you pass std::equal_to as the comparison predicate, it's not finding a match (due to an error in your code).
In the case where a match isn't found, your function ends without returning anything, which is undefined behavior. The subsequent use of this non-existent return value in the call to vector::erase results in the crash.
Your code fails to find a match because of this condition:
if (c(*f, *f + i))
You're first dereferencing the iterator and then adding i to that result. What you actually want to do is
if (c(*f, *(f + i)))
And then add a return statement at the end of the function for the case when a match is not found.
return e; // return the end iterator
Finally, your entire function can be replaced by std::adjacent_find which searches a range for two adjacent elements that meet a specified criterion. The two argument version uses operator== to perform the comparison, while you can supply a binary predicate using the three argument version. Using this, your example reduces to
#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;
int main()
{
vector<int>vstr = { 1, 2, 3, 4,4,5,5,5 };
vstr.erase(std::adjacent_find(vstr.begin(), vstr.end()));
for (auto&itr : vstr) {
cout << itr << " ";
}
}
Output:
1 2 3 4 5 5 5
Live demo
So, in the comments Alessandro Teruzzi has the key point.
are you sure you meant c(*f,*f+i) instead of c(*f,*(f+i))?
But that being said, this could be written more clearly as two nested for loops, and with longer variable names.
#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;
template<typename T, typename C> T pfind(T begin, T end, C comparator){
for (int i = 0; begin + i != end; ++i)
for (int j = i+1; begin + j != end; j++)
if (comparator(begin[i], begin[j]))
return (begin + i);
return end;
}
int main()
{
vector<int>vstr = { 1, 2, 3, 4,4,5,5,5 };
vstr.erase(pfind(vstr.begin(), vstr.end(),equal_to<int>()));
for (auto&itr : vstr){
cout << itr << " ";
}
getchar();
}
We could also write the pfind() function directly using iterators:
template<typename T, typename C> T pfind(T begin, T end, C comparator){
for (; begin != end; begin++)
for (T iterator = std::next(begin); iterator != end; iterator++)
if (comparator(*begin, *iterator))
return begin;
return end;
}
Additionally, in both cases there's a chance that we will return the end pointer. If we do, then we will call vstr.erase(vstr.end()), which is undefined behavior. So you probably want to check for that.
Let's talk about what a particular warning means:
In the comments you said that the compiler reported this warning:
main.cpp:13:1: warning: control may reach end of non-void function [-Wreturn-type]
It means that the compiler was not able to ensure that all paths through the code would end with a statement return ...; In your case, in the simplest case, if begin == end, then the first loop will not be entered, and we will hit the end of the function without reaching a return ...; statement.
In the code I've posted with fixes, you can see that if we never find a match, we return the end pointer, which is a common way of reporting no match.
#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.