Bizzare std::distance output - c++

I have taken this little snippet straight out of some code I'm working on:
KeyIter it = timeline_.lowerBound( frame );
if ( timeline_.isKeyAtFrame( frame ) ) {
++it;
}
KeyIter it1 = it - 1;
cout << "dist1: " << std::distance( timeline_.begin(), it1 ) << endl;
while ( ignore.contains( it1.key() ) ) {
cout << "dist2: " << std::distance( timeline_.begin(), it1 - 1 ) << endl;
if ( std::distance( timeline_.begin(), --it1 ) < 0 ) {
break;
}
}
cout << "dist3: " << std::distance( timeline_.begin(), it1 ) << endl;
It gives this output:
dist1: 0
dist2: 2
dist3: 2
ignore is a QSet<int> and it1 is an iterator for timeline_ (it's map type with a key of int). As you can see it1 starts at the beginning (this is correct), then control goes into the while loop where the iterator is moved backwards by one; but instead of std::distance being -1, it's 2! All that happens inbetween is a copy of the key is used to check if the QSet contains the same int.
Using a debugger I can confirm that timeline_ does not change inbetween the two dist# outputs (only one thread is running at this point in the code anyway).
Can anyone see why std::distance would be giving this output?

I'm not sure about Qt's behavior on this, but in standard library containers, aquiring an iterator outside the range [container.begin(),container.end()] is undefined behavior. I would assume it's the same in Qt, though I'm not sure. However, even if it's not, the behavior of std::distance on non-random access iterators is to count the number of increments required to get from the first iterator to the last, so this:
std::distance(x,y)
where y precedes x, is undefined behavior.

Related

How to endlessly loop over map

I need to iterate over the entire map without stopping the loop.
My example works but it uses two loops, can this be fixed?
I think it's possible to do this using only one for loop
#include <map>
map<int, int>map2;
map2[1] = 11;
map2[2] = 12;
map2[3] = 13;
for (;;)
{
for (auto& a : map2)
{
cout << a.first << " : " << a.second << '\n';
}
}
Use the std::map::iterator. That way you can just check if the iterator is at the end and if so reset it to the beginning.
map<int, int>::iterator it;
for ( it = map2.begin(); it != map2.end(); it == std::prev( map2.end() ) ? it = map2.begin() : it++ )
{
cout << it->first << " : " << it->second << '\n';
}
Clarification
it == std::prev( map2.end() ) ? it = map2.begin() : it++
That is the ternary operator. You first ask if the iterator is equal to the last element in the map. We add the std::prev() in order to get the last element as map::end() provides us with a past-the-end value.
it == std::prev(map2.end())
If it is the last element you set the iterator to the beginning of the map.
it = map2.begin()
Else the iterator is incremented and you get the next element
it++
See also: Loop through map

C++ iterate container backwards N steps

In C++ I can use reverse_iterator to loop elements of container, say, list, backwards.
How do I iterate over a certain number of elements? Not reaching the beginning? This code works, but I feel there is a better way.
std::list<int> mylist{1,2,3,4,5};
int cnt = 3;
for (auto rit = mylist.rbegin(); rit != mylist.rend(); ++rit) {
if (cnt == 0) break;
std::cout << *rit << " ";
--cnt;
}
Expected output {5, 4, 3}.
You can adjust the loop as follows:
for (auto rit = mylist.rbegin(); rit != std::next(mylist.rbegin(), 3); ++rit)
{
std::cout << *rit << " ";
}
but note that for this to work reliably, you need to check that the list is at least of size 3 or adjust the parameter to std::next as in const auto n = std::min<std::size_t>(3, mylist.size());.
With C++20, you should be able to go with (obviously not tested)
#include <ranges>
for (int n : mylist | std::ranges::reverse_view | std::ranges::take_view(3))
std::cout << n << "\n";
This renders the size testing superfluous, as take_view is bound by the range size (it performs this check internally).
Not reaching the beginning?
If you can guarantee that, then
std::copy_n(mylist.rbegin(), 3, std::ostream_iterator<int>(std::cout, " "));
Or to be safer
std::copy_n(mylist.rbegin(), std::min(mylist.size(), std::list<int>::size_type(3)), std::ostream_iterator<int>(std::cout, " "));
LIVE
I would not put incrementing/decrementing and checking the condition in the loop body, but rather put it where you most expect it:
std::list<int> mylist{1,2,3,4,5};
int cnt = 3;
for (auto rit = mylist.rbegin(); rit != mylist.rend() && cnt > 0; ++rit,--cnt) {
std::cout << *rit << " ";
}
Now you could for example add a continue anywhere in the loop body without making the loop go havoc.
PS: actually I like the other answers better, but I'll leave this one as it is minimal changes and imho incrementing / checking a loop counter inside the loop body is something to be avoided if possible (and it is possible here).

Returning the last element of a container that has no back() method in C++?

What is the best method to return the last element in a container that does not provide a back() member function, such as std::set?
Since the end() method returns an iterator to the first element after the end of the container, is the only way of grabbing the last element to decrement the iterator before dereferencing it?
Such as:
std::set<int> set = {1,2,3,4,5};
int end = *(set.end());
int beforeEnd = *(--set.end());
std::cout << "set.end() -> " << end << std::endl;
std::cout << "--set.end() -> " << beforeEnd << std::endl;
However, these both return:
set.end() -> 5
--set.end() -> 5
Is this the proper way of getting the last element, and why do these return the same value?
This
int end = *(set.end());
As commented by πάντα ῥεῖ, has undefined behavior. That's because std::set::end
Returns an iterator to the element following the last element of the container. This element acts as a placeholder; attempting to access it results in undefined behavior.
(https://en.cppreference.com/w/cpp/container/set/end, emphasis mine)
The other line:
int beforeEnd = *(--set.end());
It's not guarateed to work. See e.g. https://en.cppreference.com/w/cpp/iterator/prev, emphasis mine:
Although the expression --c.end() often compiles, it is not guaranteed to do so: c.end() is an rvalue expression, and there is no iterator requirement that specifies that decrement of an rvalue is guaranteed to work. In particular, when iterators are implemented as pointers, --c.end() does not compile, while std::prev(c.end()) does.
So it may fail for the same reason that this wouldn't compile:
int arr[4] = {1,2,3,4};
int *p = --(arr + 4); // --> error: expression is not assignable
You can write something like the following, instead.
std::set<int> set = {1,2,3,4,5};
if ( set.begin() != set.end() )
{
auto itLast = std::prev(set.end());
std::cout << "last -> " << *itLast << '\n';
}
Is the only way of grabbing the last element to decrement the iterator
before dereferencing it?
No, there are also other options.
The simplest way is using the reverse iterators(both std::set::rbegin or std::set::crbegin), which dirctly give you the element which is one past std::sets end iterator.
From cppreference.com, std::set::rbegin and std::set::crbegin
Returns a reverse iterator to the first element of the reversed
container. It corresponds to the last element of the non-reversed
container. If the container is empty, the returned iterator is equal
to rend().
std::set<int> set = { 1,2,3,4,5 };
auto iter = set.rbegin();
const int beforeEndIter = *iter;
std::cout << "--set.end() -> " << beforeEndIter << '\n';
(update) in case of the second last element in the container(i.e. two past the end iterator), use std::next for the same reason mentioned in the other answer for std::prev. See a demo
std::set<int> set = {1, 2};
const bool hasElements = set.cbegin() != set.cend();
auto iter = set.rbegin();
if(hasElements && iter != set.rend()) std::cout << "--set.end() -> " << *iter << '\n';
if(hasElements && std::next(iter) != set.rend()) std::cout << "two past set.end() -> " << *std::next(iter) << '\n';
outputs:
--set.end() -> 2
two past set.end() -> 1
The second option has been mentioned in the other answer.
On the other hand, dereferencing the end iterator is an undefined behavior, in which you could expect any result. In your case, you have got the last element(i.e. one past end iterator) of the container.

std::list reverse iterating & erasing causes crash

I am traversing an std::list using reverse iterators and erasing some elements from the list using their forward iterators that were obtained when inserting them. A sample program is shown below. I read that deleting elements from a list doesn't invalidate other iterators except for the ones referring to element that's deleted. But there's no mention of reverse_iterators and my program is crashing. Can someone please tell if the usage is incorrect?
What the program is doing is adding an element into the list, storing its iterator, reverse iterating the list and deleting the only element in the list using its stored iterator.
The output is pasted below the code sample.
#include <list>
#include <iostream>
using namespace std;
struct node
{
int data;
list<node*>::iterator iter;
} a;
int main()
{
list<node*> l;
a.data = 1;
l.push_front( &a );
a.iter = l.begin();
list<node*>::reverse_iterator ri = l.rbegin();
while ( ri != l.rend() )
{
cout << (*ri)->data << endl;
list<node*>::reverse_iterator rj = ri;
++ri;
if ( ri == l.rend() )
cout << "before erase: reached end" << endl;
l.erase((*rj)->iter);
if ( ri == l.rend() )
cout << "after erase : reached end" << endl;
else
cout << "after erase : Not reached end" << endl;
}
}
OUTPUT
1
before erase: reached end
after erase : Not reached end
610568524
before erase : reached end
Segmentation fault
Under VS2010 it will throw an exception here, on the first loop pass:
l.erase((*rj)->iter);
if ( ri == l.rend() ) // exception
That should give you a general idea what's going on. You see, reverse_iterator is just a wrapper for standard iterator. That said, you should remember that it's got base() member that returns underlying iterator - you don't have to store it elsewhere, like you do in node struct.
Here's a great answer to how reverse_iterator relates to the iterator. In your case, rbegin will be based on begin iterator. If you remove the begin from the list (which you do, since it has only one element), all reverse_iterators based on this iterator will become invalid. Keeping that in mind you could rewrite your loop the following way:
while ( ri != l.rend() )
{
cout << (*ri)->data << endl;
list<node*>::reverse_iterator rj = ri;
++ri;
if ( ri == l.rend() )
cout << "before erase: reached end" << endl;
// the actual underlying iterator has an offset of one
list<node*>::iterator it = l.erase(--rj.base());
ri = reverse_iterator<list<node*>::iterator>(it);
// or just
// ri = reverse_iterator<list<node*>::iterator>(l.erase(--rj.base()));
if ( ri == l.rend() )
cout << "after erase : reached end" << endl;
else
cout << "after erase : Not reached end" << endl;
}
The reverse iterator is (tpyically) not a unique class, but a adapter on a normal iterator - it has a iterator into a list as member and uses it to do its own moving and dereferencing. So when this list iterator gets invalidated, the reverse iterator is invalidated too.
i'm writing an answer to note my findings; if you hit this problem try
SingerOfTheFall 's suggested method, it works like a charm, example:
for(auto it=values.end();it!=values.begin();){
if((*it).second.endPoint)
break;
values.erase((*(it--)).first);
}
back to my findings about this:
when i hit the problem the program hanged, and i've run a valgrind check, it dropped out some strange Invalid reads originated from libstdc++
Invalid read of size 8
at 0x4EAA633: ??? (in /usr/lib/x86_64-linux-gnu/libstdc++.so.6.0.20)
by 0x402FEC: std::_Rb_tree_iterator<std::pair<int const, GroupControl::Group::Entry> >::operator--() (stl_tree.h:218)
i suspect that after the last element's erase the rend() doesnt stop the iterator, and the ++ op is trapped in a loop
You need store erase return value into iterator. Do following change.
(*rj)->iter= l.erase((*rj)->iter);

How to search for an element in a vector?

i have a code like this....
std::vector<string>::iterator p;
p = find(v.begin(),v.end(),"asdasda");
cout << *p << endl;
if "asdasda" is not a part of the vector, p points to some garbage and cout gives a seg fault. what should be the if statement that would make the cout execute onlyif "asdasda" was found?
and also the position of "asdasda" in v.. like if we had earlier declared v[3] as "asdasda",
then how can i know from the find that v[3] is "asdasda"?
p doesn't point to "garbage", it simply becomes v.end() when the content is not found.
if (p != v.end()) {
std::cout << "v[" << (p - v.begin()) << "] = ";
std::cout << *p << std::endl;
}
If std::find doesn't find anything, the iterator is set to v.end() in this case.
if ( p != v.end() )
{
// iterator is good
}
Also note the general case of std::find.
Here's a typical definition of it:
namespace std {
template <class InputIterator, class T>
InputIterator find(InputIterator start,
InputIterator finish,
const T& value);
}
In case std::find fails, it will return the finish iterator because the search is [start, finish) and includes start but excludes finish.
Find returns the end iterator if it doesn't find the value you search.
You can avoid your error by adding if (p != v.end()) right before your display line.