C++ list iterator arithmetic? - c++

I'm trying to create a set of loops with iterators and I'm having trouble with some iterator arithmetic (that I thought was possible but is not working).
Below is some code:
for (list<Term>::iterator itr = final.begin(); itr != final.end(); itr++) {
for(list<Term>::iterator j = itr + 1; j != final.end(); j++) {
cout << itr->term << " " << j->term;
if(itr->term == j->term) {
//Do stuff
}
}
}
What I am trying to do is have j start at the next place in the queue along from itr. The reason for this is I don't want to check the first item against itself. The error itself comes from the part in the code where I have specified itr + 1. Now I was sure with pointers you could do arithmetic like this, why is it not working with the list iterator (which is essentially the same thing?)
The error I am getting from my IDE is as follows: main.cpp:237:48: error: no match for ‘operator+’ in ‘itr + 1’. Again I thought you could do this sort of arithmetic on iterators so I'm not really sure what to do to make this work, is there an alternate implementation I could try?

list iterators are not random access so you cannot do + with them. They are bidirectional iterators so the only movement operations you can do are -- and ++. You can either make a copy and use ++ on it, or make a copy and std::advance(it, 1).
For C++11 there is also std::next which gives you it + 1, without you having to explicitly make a named copy like you do with the others.

list has bidirectional iterators, that doesn't support operator +. You can use std::advance, or std::next in C++11.
for (list<Term>::iterator j = next(itr); j != final.end(); ++j)
or
list<Term>::iterator j = itr;
advance(j, 1); // or ++j
for (; j != final.end(); ++j)

Related

c++ vector erase function not working for specific words?

I am using a very simple function in c++, vector.erase(), here's what I have (I'm trying to erase all instances of these three keywords from a .txt file):
First I use it in two separate for loops to erase all instances of <event> and </event>, this works perfectly and outputs the edited text file with no more instances of those words.
for (int j = 0; j< N-counter; j++) {
if(myvec[j] == "<event>") {
myvec.erase(myvec.begin()+j);
}
}
for (int j = 0; j< N-counter; j++) {
if(myvec[j] == "</event>") {
myvec.erase(myvec.begin()+j);
}
}
However, when I add a third for loop to do the EXACT same thing, literally just copy and paste with a new keyword as follows:
for (int j = 0; j< N-counter; j++) {
if(myvec[j] == "</LesHouchesEvents>") {
myvec.erase(myvec.begin()+j);
}
}
It compiles and executes, however it completely destroys the .txt file, making it completely un-openable, and when i cat it, I just get a bunch of crazy symbols.
I have tried switching the order of these for loops, even getting rid of the first two for loops entirely, everything I can think of, alas it just will not work for the keyword </LesHouchesEvents> for some strange reason.
Your loops are not taking into account that when you erase() an element from a vector, the indexes of the remaining elements will decrement accordingly. So your loops will eventually exceed the bounds of the vector once you have erased at least 1 element. You need to take that into account:
std:string word = ...;
size_t count = N-counter;
for (int j = 0; j < count;) {
if(myvec[j] == word) {
myvec.erase(myvec.begin()+j);
--count;
}
else {
++j;
}
}
With that said, it would be safer to use iterators instead of indexes. erase() returns an iterator to the element that immediately follows the removed element. You can use std::find() for the actual searching:
#include <algorithm>
std::vector<std::string>::iterator iter = std::find(myvec.begin(), myvec.end(), word);
while (iter != myvec.end())
{
iter = myvec.erase(iter);
iter = std::find(iter, myvec.end(), word);
}
Or, you could just use std::remove() instead:
#include <algorithm>
myvec.erase(std::remove(myvec.begin(), myvec.end(), word), myvec.end());
I don't know if this is your specific problem or not, but this loop is almost surely not what you want.
Note the documentation for erase - it "shifts" left the remaining elements. Unfortunately, your code still increments j, meaning you're skipping the next element:
for (int j = 0; j< N-counter; j++) { // <- Don't increment j here
...
myvec.erase(myvec.begin()+j); // <- increment it only if this didn't happen.
}
You'll also need to adjust your loop's halting condition.
Even assuming you got it working, this is nearly the worst possible way to remove items from a vector.
You almost certainly want the remove/erase idiom here, and you probably want to do all the comparisons in a single pass, so it's something like this:
std::vector<std::string> bad = {
"<event>",
"</event>",
"</LesHouchesEvents>"
};
myvec.erase(std::remove_if(my_vec.begin(), my_vec.end(),
[&](std::string const &s) {
return std::find(bad.begin(), bad.end(), s) != bad.end();
}),
my_vec.end());

C++ iterating with changing vector.size()

I've written some perhaps naive code that is meant to remove elements from a vector that are too similar. The functionality is fine, but I think I may get unexpected results now and then because of the dynamic resizing of the vector.
for (size_t i = 0 ; i < vec.size(); i++) {
for(size_t j = i+1; j < vec.size(); j++) {
if(norm(vec[i]-vec[j]) <= 20 ) {
vec.erase(vec.begin()+j);
}
}
}
Is this safe to do? I'm concerned about i and j correctly adapting as I erase elements.
You need to pay better attention to where your elements are. It might be easier to express this directly in terms of iterators rather than compute iterators via indexes, like this:
for (auto it = vec.begin(); it != vec.end(); ++it)
{
for (auto jt = std::next(it); jt !=; vec.end(); )
{
if (/* condition */)
{
jt = vec.erase(jt);
}
else
{
++jt;
}
}
}
Yes, you are safe here. Since you are using indexes, not iterators, there is nothing to invalidate by erasing an item in the container except the size, and the size would be updated automatically, so we are good here.
One more thing to consider is what effect does erasing an element inside the inner loop has on the stopping condition of the outer loop. There is no problem there either, because j is guaranteed to be strictly greater than i, so j < vec.size() condition of the inner loop will be hit before the i < vec.size() condition of the outer loop, meaning that there would be no unsafe vec[i] access with an invalid index i.
Of course you should increment j after erasing an element to avoid the classic error. An even better approach would be to start walking the vector from the back, but you would need to do so in both loops to make sure that i a valid element is never erased from underneath the outer index of i.

Insertion sort issues?

I'm just now trying out different sorting algorithms to find out how they work.
I'm looking at this one:
template< typename Iterator >
void insertion_sort( Iterator First, Iterator Last )
{
Iterator min = First;
for( Iterator i = First + 1; i < Last; ++i )
if ( *i < *min )
min = i;
std::iter_swap( First, min );
while( ++First < Last )
for( Iterator j = First; *j < *(j - 1); --j )
std::iter_swap( (j - 1), j );
}
It's the Insertion Sort and it sorts them from smallest to largest, however I have 2 issues:
Reversing any comparison sign doesn't change the sort order to descending.
When implementing it with my own container, it never sorts the last element. Maybe I didn't get how Begin and End should work for a container? This is how I make them:
If I have a dynamic array container that has an unsigned int size and a T* data, Begin returns an iterator that has a pointer to data[0] and End returns an iterator with a pointer to data[size - 1]. If I make end to instead return data[size], it works fine, however if the size is equal to the allocated capacity, that would overflow the buffer. So the presented insertion sort algorithm is incorrect then? I got it from this site:
http://en.wikibooks.org/wiki/Algorithm_Implementation/Sorting/Insertion_sort#C.2FC.2B.2B
About question 1: I think it should work if you reverse all comparison signs that involve dereferenced iterators, i.e., replace
if ( *i < *min )
by
if ( *i > *min )
(one might also replace the name 'min' by 'max' then to avoid confusion), and
for( Iterator j = First; *j < *(j - 1); --j )
by
for( Iterator j = First; *j > *(j - 1); --j )
These are the comparisons that compare the actual data.
About question 2: usually, an 'end' iterator refers to the position behind the last actual item that you want to sort. Standard algorithms (such as yours) will never dereference this iterator, so no overflow will occur.
The iterators pointing to an end() are one past the last element, so that you'd usually loop through them like:
for(Iterator it = begin(); it != end(); ++it) ...
Your iterator implementation thus stops one before that last and is the culprit.
It might be a good idea not to test everything at once, i.e. a custom algorithm with custom iterators. You can use a stl container to test the algorithm and an stl algorithm to test your container.

Equating iterators

I'm facing some problems with set::iterators in c++. I have a set of 2-D points and I need to iterate and compare these points three by three.
My thought was to use three loops incrementing the iterators, one by one. But c++ gave me an error when I tried:
for (set<pair<double, double> >::iterator i = S.begin(); i != S.end(); i++){
for (set<pair<double, double> >::iterator j = i+1; j != S.end(); j++){
...
}
}
Edit: the error was "error: no match for ‘operator+’ in ‘i + 1’"
std::set has a bidirectional iterator. It has no such operation as iterator + integer number. So use instead
for (set<pair<double, double> >::iterator j = std::next( i ); j != S.end(); j++){

accessing a specific point of a vector with an iterator

I am trying to figure out the best way of accessing a position in a vector using an iterator. I'm aware iterators behave like pointers, so this is the only method I came up with. I would like to know if there's a better or just a different way. Here's the code:
//This is a pointer to a vector of the class Particle BTW. vector < Particle > *particleList;
vector<Particle>::iterator it = particleList->begin();
// I assign a specific position outside the loop to a new iterator that won't be affected
vector<Particle>::iterator it2 = particleList->begin() + 3;
for( it; it != particleList->end(); it++){
it->draw();
//I'm interested in the velocity of this element in particular
cout << it2->vel << endl;
}
Thanks,
M
Try the following
for (auto i = particleList->begin(); i < particleList->begin(); ++i) {
i->draw();
std::cout << (i+3)->vel << "\n";
}
Note, there is no reason to use std::endl, std::endl has an implicit flush which lowers performance when outputting to say a log file, and when outputting to console it is already line buffered meaning that a line ending will already flush.
Note 2, you can only use + with i since i is a random access iterator because particleList is a std::vector, if you change say particleList to a std::list then the iterator will be a bidirectional iterator instead of a random access iterator and you will not be able to use + in that case you would need to use std::advance like WhozCraig mentioned, but do so on a copy like so:
for (auto i = particleList->begin(); i < particleList->begin(); ++i) {
i->draw();
auto i2 = i;
std::advance(i2, 3)
std::cout << i2->vel << "\n";
}
Though personally, in this case I would just iterate with two iterators instead of std::advance since std::advance is linear in time. Do something like:
auto i = particleList->begin();
auto i2 = particleList->begin();
std::advance(i2, 3);
for (; i < particleList->end(); ++i, ++i2) {
i->draw();
std::cout << i2->vel << "\n";
}
Note 3: (i+3) and i2 will run off the end of your list (vector), so do something smart there.