Short question: Is the following code unsafe using other compilers than I do (mingw32), or is it valid to use?
list<int> l;
/* add elements */
list<int>::iterator i = l.begin();
i--;
i++;
cout << *i << endl;
...or in other words: is i defined to point to l.begin() after this?
Yes, the code is unsafe. Once you attempt to move before begin() you have caused undefined behavior. Attempting to move "back again" may not work.
A std::list traverses its contents via linked list pointers, so pointer arithmetic is not used to calculate a correct position. The previous position from .begin() will have no data and shouldn't provide any valid traversal mechanisms.
Containers like std::vector have random access iterators and would use pointer arithmetic under the covers, so they would probably give the right result (no problem), but its still a bad idea.
So, it shouldn't work, its undefined, and don't do it even if it does work somehow :)
Related
list<pair<int,int>> li{{5,6},{7,8},{9,10}};
for(auto it=li.rbegin();it!=li.rend();++it) {
cout << (*it).first << (*it).second << '\n';
li.erase(it);
}
error: no matching function for call to 'std::list<std::pair<int, int> >::erase(std::reverse_iterator<std::_List_iterator<std::pair<int, int> > >&)'
li.erase(it);
If the li.erase(it) is replaced by li.remove(*it), the code works fine. However, all the value will be removed., and the algorithm become O(n2), instead of O(n), right?
Erasing while iterating is a bad, bad idea. If it is no longer in the list, what will ++it do? Good question! I don't know, but it's not what you want unless you got unlucky. Why is it unlucky if the program works? Because it doesn't, and next time it might do something completely different.
So pretty much all of the loop-based iterate and delete approaches that seem to work (li.remove(*it) and li.erase( --(it.base()) ) actually don't.
Normally I pitch the erase-remove idiom to solve that problem, but the reverse iterator makes this a mess if you try to use C++'s built-in tools. Plus in this case the answer to remove_if is always true.
For the problem posed above, don't even try. Instead, print li.back() and then li.pop_back() until the list is empty.
while (!li.empty()) {
cout << li.back().first << li.back().second << '\n';
li.pop_back();
}
You can replace li.erase(it) with li.erase(it.base()).
std::list.erase does not allow a reverse_iterator as an argument, so you first need to convert the reverse_iterator into an iterator.
And you're correct, as far as I can tell, about the O(n^2) and O(n) analysis.
EDIT:
Actually, according to this similar answer: How to call erase with a reverse iterator
What you should really do is li.erase( --(it.base()) )
std::vector<struct::event>::iterator it;
std::vector<struct::event>::iterator last=myvector.end();
for (it=myvector.begin(); it<=last; it++){
if(mysignal.declination<(*last).declination){
if (mysignal.declination>=(*it).declination && mysignal.declination<(*(it+1)).declination){
myvector.insert(it+1, mysignal);
break;
}
}
if (mysignal.declination>=(*last).declination){
myvector.push_back(mysignal);
break;
}
}
I have a vector called myvector with events that are sorted with the declination. now I want to add mysignal to this vector on the right place. but i always get a seg fault after a few events which refers to: if(mysignal.declination<(*last).declination). I just can't see what is wrong.
Your loop is wrong, read the docs:
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.
You can't dereference end(), it provides a way of knowing that you have overrun the container, so your loop condition should be it != myvector.end(), and last is wrong as well.
As others have said, C++ iterators define a half-open interval
('[begin()...end())'), which is what you should probably be
using in most other cases as well. And although it works with
iterators from a vector, in general, iterators do not support
<= (nor <); the standard idiom for a loop is:
for ( auto current = container.begin();
current != container.end();
++ current ) ...
(In the most likely case that you cannot count on C++11, you'll
have to write out the full iterator type, rather than use
auto. Although auto is one of the few things from C++11
that seems to work with VC++11 and with recent versions of
g++, so if those are the only targets you're concerned with, and
you can be sure of always having very recent versions, you can
use it.)
Also, if you want to access the last element of the vector in
the loop, myvector.back() will return a reference to it.
(myvector.back() is undefined behavior if the vector is empty,
but if the vector is empty, you won't enter the loop.)
end() does not refer to the last element in the container, you need to change your condition as follows.
for (it=myvector.begin(); it != last; it++){
You have other broken logic as well that is dereferencing last that you need to fix.
In the code of my game, I want to remove some elements from a list,
which happens in a loop.The only problem I have is, when I use
list::erase I have to break after that function because I think
the list becomes "outdated". This causes a little flicker and I want to
try to remove it.
The current code is this:
for(list<Arrow*>::iterator it = arrows.begin(); it != arrows.end(); it++)
{
Arrow* a = (*it);
if(a->isActive() == true)
{
a->update();
}
else
{
arrows.erase(it);
break;
}
}
Thank you in advance!
Edit:
Sorry, I was confused with vector and list. Got the answer, thanks!
you should do:
it = arrows.erase(it);
//
list<Arrow*>::iterator it = arrows.begin();
while (it != arrows.end())
{
Arrow* a = (*it);
if(a->isActive())
{
a->update(); ++it;
}
else{ // delete (a); ???
it=arrows.erase(it);}
}
I am confused, you say vector and in your example you are using a list.
List is implemented with a double linked-list (actually it is likely to be implemented, because the standard fix just the complexity and not the details). The iterator after erasing are still valid. http://www.cplusplus.com/reference/stl/list/erase/
Erasing in the middle with vector is slow and also invalided all the iterators and references.
Like everyone said you example is confusing. If you want delete in the mid of your container and if you are using
1>vector:
Then every iterator and reference after the point of erase is invalidated.
Also vector deleting from mid of vector will causing element behind it to shift which might considered slow if you want performance.
2>list:
Then only the deleted iterator and reference is invalidated.
Either way using the erase-remove idiom is preferred when you want to delete some element in the middle of stl sequence container.
The proper way to filter out items in a standard library container is to use the Erase-Remove Idiom. Because you're using a member function as the test in the loop, you should adapt the example to use std::remove_if().
You can implement this in a short and sweet (provided you like functional programming) way:
#include <algorithm>
#include <functional>
arrows.erase(std::remove_if(
arrows.begin(), arrows.end(), std::mem_fun(&Arrow::isActive)
));
This will work for std::vector<>, std::deque<>, std::list<> etc. regardless of implementation and iterator invalidation semantics.
Edit: I see you're also using the Arrow::update() method inside the loop. You can either do a double pass on the list, use a function object to call both methods or write the loop manually.
In the last case, you can use the it = arrows.erase(it); trick, but this will only be efficient for std::list<>. The loop will have O(n^2) complexity for the
std::vector<> and std::deque<> containers.
I am a big fan of GCC, but recently I noticed a vague anomaly. Using __gnu_cxx::__normal_iterator (ie, the most common iterator type used in libstdc++, the C++ STL) it is possible to refer to an arbitrary memory location and even change its value without causing an exception! Is this expected behavior? If so, isn't a security loophole?
Here's an example:
#include <iostream>
using namespace std;
int main() {
basic_string<char> str("Hello world!");
basic_string<char>::iterator iter = str.end();
iter += str.capacity() + 99999;
*iter = 'x';
cout << "Value: " << *iter << endl;
}
Dereferencing an iterator beyond the end of the container from which it was obtained is undefined behavior, and doing nothing is just a possibility there.
Note that this is a question of compromise, it is nice having iterators check for validity for development, but that adds extra operations to the code. In MSVS iterators are by default checked (they will verify that they are valid and fail hard when they are used in a wrong way=. But that also has an impact in runtime performance.
The solution that Dinkumware (STL inside VS) provides (checked by default, can be unchecked through compiler options) is in fact a good choice, the user selects whether he wants slow safe iterators or fast unsafe versions of it. But from the point of view of the language, both are valid.
No, this is not a problem. Keep in mind that typical iterator usage is:
for ( type::const_iterator it = obj.begin(); it != obj.end(); ++it ){
// Refer to element using (*it)
}
Proper iterator usage requires one to check against the end() iterator. With random access iterators such as the one you are using, you can also use < and > with the iterators against end(). C and C++ don't typically do bounds checking as in Java, and it is your place to ensure that you do so.
C++ generally has a philosophy of not making you pay for what you don't use. It is up to you to validate that you're using iterators properly. For a random-access iterator, you can always test it:
if (iter < str.begin() || iter >= str.end())
throw something;
You got lucky. Or unlucky. Using your exact example, I segfaulted.
$ ./a.exe
11754 [main] a 4992 _cygtls::handle_exceptions: Error while dumping state (probably corrupted stack)
Segmentation fault (core dumped)
Undefined behavior can mean different things on different compiles, platforms, days. Perhaps when you ran it, the address created by all that adding ended up in some other valid memory space, just by chance. Maybe you incremented from the stack to the heap for example.
This is a question that goes to how BOOST_FOREACH checks it's loop termination
cout << "Testing BOOST_FOREACH" << endl;
vector<int> numbers; numbers.reserve(8);
numbers.push_back(1); numbers.push_back(2); numbers.push_back(3);
cout << "capacity = " << numbers.capacity() << endl;
BOOST_FOREACH(int elem, numbers)
{
cout << elem << endl;
if (elem == 2) numbers.push_back(4);
}
cout << "capacity = " << numbers.capacity() << endl;
gives the output
Testing BOOST_FOREACH
capacity = 8
1
2
3
capacity = 8
But what about the number 4 which was inserted half way through the loop? If I change the type to a list the newly inserted number will be iterated over. The vector push_back operation will invalidate any pointers IF a reallocation is required, however that is not happening in this example. So the question I guess is why does the end() iterator appear to only be evaluated once (before the loop) when using vector but has a more dynamic evaluation when using a list?
Under the covers, BOOST_FOREACH uses
iterators to traverse the element
sequence. Before the loop is executed,
the end iterator is cached in a local
variable. This is called hoisting, and
it is an important optimization. It
assumes, however, that the end
iterator of the sequence is stable. It
usually is, but if we modify the
sequence by adding or removing
elements while we are iterating over
it, we may end up hoisting ourselves
on our own petard.
http://www.boost.org/doc/libs/1_40_0/doc/html/foreach/pitfalls.html
If you don't want the end() iterator to change use resize on the vector rather than reserve.
http://www.cplusplus.com/reference/stl/vector/resize/
Note that then you wouldn't want to push_back but use the operator[] instead. But be careful of going out of bounds.
The question was raised in the comments as to why the Microsoft debug runtime raises an assertion during iteration over the vector but not over the list. The reason is that insert is defined differently for list and vector (note that push_back is just an insert at the end of the sequence).
Per the C++ standard (ISO/IEC 14882:2003 23.2.4.3, vector modifiers):
[on insertion], if no reallocation happens, all the iterators and references before the insertion point remain valid.
(23.2.2.3, list modifiers):
[insert] does not affect the validity of iterators and references.
So, if you use push_back (and are sure that it's not going to cause a reallocation), it's okay with either container to continue using your iterator to iterate over the rest of the sequence.
In the case of the vector, however, it's undefined behavior to use the end iterator that you obtained before the push_back.
This is a roundabout answer to the question; it's a direct answer to the discussion in the question's comments.
boost's foreach will terminate when it's iterator == numbers.end()
Be careful though, calling push_back can/will invalidate any current iterators you have.