Reputation: 10152
I have written a short code that uses a while loop to go through a (sorted!) vector of structures and inserts a new element based on its price (if there is already an entry for a specific price, the new entry should be inserted behind the existing entry.
Using g++
and Sublime Text 3 everything works (output at the end of this question), but using Visual Studio 2015 I get the error: Debug Assertion Failed! ... Expression: vector iterator not dereferencable
. (note that this error only surfaces if the new element should be attached at the end of the existing vector, if we change the new element to struc newEl = {5, 2.0}
everything works fine with both VS and g++
).
The code looks like this:
#include <iostream>
#include <vector>
struct struc {
int num;
double price;
};
int main() {
std::vector<struc> v = { { 1, 1.5 },{ 2, 1.6 },{ 3, 2 },{ 4, 2.6 } };
struc newEl = { 5, 2.7 };
std::vector<struc>::iterator it = v.begin();
while (((*it).price <= newEl.price) && (it < v.end())) {
it++;
}
v.insert(it, newEl);
for (struc vec : v) {
std::cout << vec.num << ", " << vec.price << std::endl;
}
std::cout << "Finished";
return 0;
}
The output should be (and is in the case of g++
and ST):
1, 1.5
2, 1.6
3, 2
4, 2.6
5, 2.7
Finished
Now my question is, what causes the error in VS and what do I need to change to make it work in VS?
Thank you very much!
Upvotes: 3
Views: 2372
Reputation: 19767
while (((*it).price <= newEl.price) && (it < v.end())) {
You need to check if you are at the end before you dereference the iterator and check the value:
while ( (it!=v.end()) && (it->price <= newEl.price)) {
Otherwise you are basically guaranteeing you will dereference the end
iterator, which is precisely what you are trying to avoid.
(You can't use <
on most types of iterator, so checking the loop with !=
is a more common practice).
You may be aware that &&
"short-circuits" so, if the first condition is false
, the second one doesn't even get checked. This is guaranteed by the language (unless some idiot overloads operator&&
, but that is thankfully rare, because it is such a bad idea). That is why my version is safe.
Now, this mistake triggers Undefined Behaviour - the error is on you! The language doesn't even try to help you, and anything can happen. Your program could crash, it could appear to work normally, it could work every other time, or it could turn your laptop into a pink giraffe. C++ trades being nice to you against being fast.
I know the Microsoft implementation, in debug mode, has checked iterators which are catching your mistake here, at the cost of always checking. In release mode this wouldn't happen.
I've not used it, but according to this answer, GCC does have something similar.
(As an aside, I really consider use of std::endl
to be bad practice. Here's why. Some people do disagree with me on that. Learn about it and make your own decision, I suppose.)
Upvotes: 6