Vector not dereferencable - c++

After having looked at the comments I looked through the code and found an error.
It seems after some tinkering I got faced with this error:
Debug error: vector iterator is not dereferencable.
I'm 100% certain that it is in the vector inside assingthreads.
This is the newly added code that spawns the error:
void historical::writeData(std::vector<std::vector<std::wstring>> in, const string& symbol) {
std::cout << "Sending data to database connector" << std::endl;
std::vector<std::vector<std::wstring>> temp;
while (!in.empty()) {
for (int i = 0; i < 5; i++) {
temp.push_back(in.back());
in.pop_back();
}
assignthreads(temp, symbol);
temp.clear();
}
}
void historical::assignthreads(std::vector<std::vector<std::wstring>> partVec, const string& symbol) {
int i = 0;
std::thread threads[5];
std::vector<std::vector<std::wstring>>::iterator it;
for (it = partVec.end();
it != partVec.begin();
it--) {
std::shared_ptr<database_con> sh_ptr(new database_con);
threads[i] = std::thread(&database_con::start, sh_ptr, *it, symbol);
partVec.pop_back();
i++;
}
for (auto& th : threads) th.join();
}

Your first time through the for-loop, it = partVec.end().
By definition you cannot dereference the end of a vector but you call:
threads[i] = std::thread(&database_con::start, sh_ptr, *it, symbol);
The for loop you intended probably used reverse iterators, rbegin and rend like this:
for(auto it = rbegin(partVec); it != rend(partVec); ++it)
A couple additional notes:
Pass your vector by reference: void assignthreads(std::vector<std::vector<std::wstring>>& partVec, const string& symbol)
You need to validate that threads is the same size as partVec. So either do: vector<thread> threads(size(partVec)) or after threads is defined do: assert(size(threads) == size(partVec))

At least one issue with the for loop in assignthreads is that you attempt to dereference the end() of the vector;
for (it = partVec.end(); it != partVec.begin(); it--) {
// ...
threads[i] = std::thread(&database_con::start, sh_ptr, *it, symbol);
// ^^^^
}
And on the first iteration of the loop this is undefined; your debugger is just telling you that.
If you want to "reverse" through the loop, use the reverse_iterator of the container (available via rbegin() and rend())
for (it = partVec.rbegin(); it != partVec.rend(); ++it)
Side note it is generally not advised to modify the container whilst iterating through it (via partVec.pop_back();). Since you don't seem to do anything with what is removed from the vector, it may just as well be better to iterate over the contents, and then call std::vector<>::clear() to remove all the contents from the vector after the loop.

Related

How to repair SigSegV [duplicate]

I want to clear a element from a vector using the erase method. But the problem here is that the element is not guaranteed to occur only once in the vector. It may be present multiple times and I need to clear all of them. My code is something like this:
void erase(std::vector<int>& myNumbers_in, int number_in)
{
std::vector<int>::iterator iter = myNumbers_in.begin();
std::vector<int>::iterator endIter = myNumbers_in.end();
for(; iter != endIter; ++iter)
{
if(*iter == number_in)
{
myNumbers_in.erase(iter);
}
}
}
int main(int argc, char* argv[])
{
std::vector<int> myNmbers;
for(int i = 0; i < 2; ++i)
{
myNmbers.push_back(i);
myNmbers.push_back(i);
}
erase(myNmbers, 1);
return 0;
}
This code obviously crashes because I am changing the end of the vector while iterating through it. What is the best way to achieve this? I.e. is there any way to do this without iterating through the vector multiple times or creating one more copy of the vector?
Use the remove/erase idiom:
std::vector<int>& vec = myNumbers; // use shorter name
vec.erase(std::remove(vec.begin(), vec.end(), number_in), vec.end());
What happens is that remove compacts the elements that differ from the value to be removed (number_in) in the beginning of the vector and returns the iterator to the first element after that range. Then erase removes these elements (whose value is unspecified).
Edit: While updating a dead link I discovered that starting in C++20 there are freestanding std::erase and std::erase_if functions that work on containers and simplify things considerably.
Calling erase will invalidate iterators, you could use:
void erase(std::vector<int>& myNumbers_in, int number_in)
{
std::vector<int>::iterator iter = myNumbers_in.begin();
while (iter != myNumbers_in.end())
{
if (*iter == number_in)
{
iter = myNumbers_in.erase(iter);
}
else
{
++iter;
}
}
}
Or you could use std::remove_if together with a functor and std::vector::erase:
struct Eraser
{
Eraser(int number_in) : number_in(number_in) {}
int number_in;
bool operator()(int i) const
{
return i == number_in;
}
};
std::vector<int> myNumbers;
myNumbers.erase(std::remove_if(myNumbers.begin(), myNumbers.end(), Eraser(number_in)), myNumbers.end());
Instead of writing your own functor in this case you could use std::remove:
std::vector<int> myNumbers;
myNumbers.erase(std::remove(myNumbers.begin(), myNumbers.end(), number_in), myNumbers.end());
In C++11 you could use a lambda instead of a functor:
std::vector<int> myNumbers;
myNumbers.erase(std::remove_if(myNumbers.begin(), myNumbers.end(), [number_in](int number){ return number == number_in; }), myNumbers.end());
In C++17 std::experimental::erase and std::experimental::erase_if are also available, in C++20 these are (finally) renamed to std::erase and std::erase_if (note: in Visual Studio 2019 you'll need to change your C++ language version to the latest experimental version for support):
std::vector<int> myNumbers;
std::erase_if(myNumbers, Eraser(number_in)); // or use lambda
or:
std::vector<int> myNumbers;
std::erase(myNumbers, number_in);
You can iterate using the index access,
To avoid O(n^2) complexity
you can use two indices, i - current testing index, j - index to
store next item and at the end of the cycle new size of the vector.
code:
void erase(std::vector<int>& v, int num)
{
size_t j = 0;
for (size_t i = 0; i < v.size(); ++i) {
if (v[i] != num) v[j++] = v[i];
}
// trim vector to new size
v.resize(j);
}
In such case you have no invalidating of iterators, complexity is O(n), and code is very concise and you don't need to write some helper classes, although in some case using helper classes can benefit in more flexible code.
This code does not use erase method, but solves your task.
Using pure stl you can do this in the following way (this is similar to the Motti's answer):
#include <algorithm>
void erase(std::vector<int>& v, int num) {
vector<int>::iterator it = remove(v.begin(), v.end(), num);
v.erase(it, v.end());
}
Depending on why you are doing this, using a std::set might be a better idea than std::vector.
It allows each element to occur only once. If you add it multiple times, there will only be one instance to erase anyway. This will make the erase operation trivial.
The erase operation will also have lower time complexity than on the vector, however, adding elements is slower on the set so it might not be much of an advantage.
This of course won't work if you are interested in how many times an element has been added to your vector or the order the elements were added.
There are std::erase and std::erase_if since C++20 which combines the remove-erase idiom.
std::vector<int> nums;
...
std::erase(nums, targetNumber);
or
std::vector<int> nums;
...
std::erase_if(nums, [](int x) { return x % 2 == 0; });
If you change your code as follows, you can do stable deletion.
void atest(vector<int>& container,int number_in){
for (auto it = container.begin(); it != container.end();) {
if (*it == number_in) {
it = container.erase(it);
} else {
++it;
}
}
}
However, a method such as the following can also be used.
void btest(vector<int>& container,int number_in){
container.erase(std::remove(container.begin(), container.end(), number_in),container.end());
}
If we must preserve our sequence’s order (say, if we’re keeping it sorted by some interesting property), then we can use one of the above. But if the sequence is just a bag of values whose order we don’t care about at all, then we might consider moving single elements from the end of the sequence to fill each new gap as it’s created:
void ctest(vector<int>& container,int number_in){
for (auto it = container.begin(); it != container.end(); ) {
if (*it == number_in) {
*it = std::move(container.back());
container.pop_back();
} else {
++it;
}
}
}
Below are their benchmark results:
CLang 15.0:
Gcc 12.2:

using a std::vector<Particle> particles; function .at() not working with iterator it as parameter inside a for loop

class Particles {
constexpr static int particleNum = 25;
constexpr static double gravity = 1.1;
std::vector<Particle> particles;
std::vector<Particle>::iterator it = particles.begin();
};
I am trying to create the 25 particles that are specified above and for that I'm using the it iterator in the for loop which works fine but when the particles.at(it) is used the console outputs an error code that says:
error: no matching function for call to
'std::vector::at(std::vector::iterator&)'
if (!particles.at(it).life){
I have tried using a simple integer for this task but then I have the particles.erase(it) not working as it needs an it_&; just take a look:
Particles::Particles(sf::RenderWindow& renderWindow, int x, int y) {
for(unsigned int i = 0; i <= particleNum; i++){
particles.push_back(Particle(x, y));
}
do{
for(; it <= particles.end();){
if (!particles.at(it).life){
it = particles.erase(it);
}else{
particles.at(it).update();
it++;
}
renderWindow.draw(particles.at(it).particleShape);
}
}while(!particles.empty());
// to change later for different effects:
}
Without modifying the code you have created beyond your context, thedo while loop can be done like so:
unsigned int ctr = 0;
do{
for(; it != particles.end(); ++it){
++ctr;
if (!particles.at(ctr).life){
it = particles.erase(it); //keep in mind erase invalidates all iterators from [it:end)
}else{
particles.at(ctr).update();
it++;
}
}
}while(!particles.empty());
Additionally, there's a few other ways you could achieve the desired effect. For example, using just the counter instead, and using particles.begin() + ctr to specify the offset; with proper checks of course that it isn't beyond particles.end(). Another option instead of using at, is to access the iterator if not end as well. For example:
do{
for(; it != particles.end(); ++it){
if (!it->life){
particles.erase(it); //erase this item here
it = particles.begin(); //reinitialize the iterator to beginning to continue searching
}
else{
it->update();
it++;
}
}
}while(!particles.empty());
Otherwise, yet another possibility is to call particles.back().update() as required and use pop_back after the update call, or once checking to see if you need to remove it is completed.
There are probably other more/less obvious ways to do the same thing as well.
You are trying to pass an iterator to the at method, but that method has a parameter of type size_type, not std::vector::iterator&. You need to call the method with a simple index, not an iterator.

referencing an object using auto iterator

unordered_map<char,int> letter;
for (auto it = letter.begin(); it != letter.end() ; it++) {
if (it->second) return false;
}
for (auto it : letter) {
if (it.second) return false;
}
Above, there are 2 iterator loops which I believe output the same thing. I can understand that the it in the first loop points to the object in the unordered_map, so the second variable must be referenced with ->. But I dont understand how the second loop can do .second. Can anyone explain how to 2nd loop works?
The second loop is a range-based for loop. It is not returning an iterator, but is instead returning a copy of the key-value pair (pair<char, int>), so it does not need to ues a -> operator to access the values.
Your range-based for would be equivalent to this, only less verbose, of course.
for (auto it = letter.begin(); it != letter.end() ; it++) {
auto kvp = *it;
if (kvp.second) return false;
}

Converting const auto & to iterator

A number of posts I've read lately claim for(const auto &it : vec) is the same as using the longer iterator syntax for(std::vector<Type*>::const_iterator it = vec.begin(); it != vec.end(); it++). But, I came upon this post that says they're not the same.
Currently, I'm trying to erase an element in a for loop, after it is used, and wondering if there is any way to convert const auto &it : nodes to std::vector<txml::XMLElement*>::iterator?
Code in question:
std::vector<txml2::XMLElement *> nodes;
//...
for (const auto &it : nodes)
{
//...
nodes.erase(it);
}
I pretty sure I could just rewrite std::vector<txml2::XMLElement*> as a const pointer, but would prefer not to since this code is just for debugging in the moment.
You should not be attempting to convert the range declaration in your range based for loop to an iterator and then deleting it whilst iterating. Even adjusting iterators while iterating is dangerous, and you should instead rely on algorithms.
You should use the Erase-remove idom.
You can use it with remove_if.
It would look something like:
nodes.erase( std::remove_if(nodes.begin(), nodes.end(), [](auto it){
//decide if the element should be deleted
return true || false;
}), nodes.end() );
Currently in the technical specifications, is erase_if.
This is a cleaner version of the same behaviour shown above:
std::erase_if(nodes,[](auto it){
//decide if the element should be deleted
return true || false;
});
You don't get an iterator but a reference to the element. Unless you want to do a std::find with it, it's pretty hard to get an iterator out of it.
Vectors are nice, so you could increase a counter per element and do nodes.begin() + counter to get the iterator, but it'd sort of defeat the point.
Also erasing the iterator in the for loop will result in you iterating after the end of the vector, you can test this code:
#include <iostream>
#include <vector>
using namespace std;
int main() {
vector<int> v = {0,1,2,3,4,5,6};
for (int x : v) {
cout << x << endl;
if (x == 2) {
v.erase(v.begin() + 2);
}
}
return 0;
}
If you want to use iterators, just do a loop with them, if in addition you want to erase one mid-loop you have to follow this answer:
for (auto it = res.begin() ; it != res.end(); ) {
const auto &value = *it;
if (condition) {
it = res.erase(it);
} else {
++it;
}
}
Note that you don't need to specify the whole type of the iterator, auto works just as well.

Moving object to front of vector c++

I have a vector<Suggestions> finalSuggestions that contains a string word and some int num.
If this word meets some condition, I want to move that object to the front of the vector, removing it from wherever it was.
I am able to insert to the beginning of the list with vector::insert
for (auto &x: finalSuggestions) {
if ( double((x.num)/(topword.num)) < 50)
{
finalSuggestions.insert(finalSuggestions.begin(),x);
break;
}
}
But I do not know how to remove it from where it is in the list.
For example, for some arbitrary vector { 1,2,3,4,50,6,7,8,9 },
if 50 meets the criteria, move it to the front of the list and delete it from where it was, returning { 50,1,2,3,4,6,7,8,9 }. The code above returns { 50,1,2,3,4,50,6,7,8,9 }
I was looking into vector::erase, but I'm having problems, and its taking longer than it should.
I envision a simple solution (but this obviously doesn't work)
for (auto &x: finalSuggestions) {
if ( double((x.num)/(topword.num)) < 50)
{
finalSuggestions.insert(finalSuggestions.begin(),x);
finalSuggestions.erase(x);
break;
}
}
I read up on the erase-remove idiom (here is my implementation):
finalSuggestions.erase( remove( begin(finalSuggestions), end(finalSuggestions), x ), end(finalSuggestions) );
but am getting an error that I don't understand:
In instantiation of '_FIter std::remove(_FIter, _FIter, const _Tp&) [with _FIter = __gnu_cxx::__normal_iterator<Suggestion*, std::vector<Suggestion> >; _Tp = Suggestion]':|
Use std::rotate. It's a lot faster than deleting and reinserting.
Eg:
for (auto it = finalSuggestions.begin(), lim = finalSuggestions.end();
it != lim;
++it) {
if (it->num < 50 * topword.num) {
std::rotate(finalSuggestions.begin(), it, it + 1);
break;
}
}
Even better, as #JerryCoffin suggests in a comment, use std::find_if to find the pivot:
auto pivot = std::find_if(finalSuggestions.begin(),
finalSuggestions.end(),
[&topword](const Suggestions& s) -> bool {
return s.num < 50 * topword.num;
});
if (pivot != finalSuggestions.end()) {
std::rotate(finalSuggestions.begin(), pivot, pivot + 1);
}
For vector::erase you need an iterator, so range-based for can't be used. Use simple for loop instead. First erase an element, and then insert it, because insert invalidates iterators:
for (auto it = finalSuggestions.begin(); it != finalSuggestions.end(); ++it) {
if (some_condition(*it)) {
auto x = *it; // or std::move(*it)
finalSuggestions.erase(it);
finalSuggestions.insert(finalSuggestions.begin(), x /* or std::move(x) */);
break;
}
}
Using std::move will allow you to move an element around instead of copying it, which may save you some cycles.
Your iterator makes it difficult to know the position of the element in question. You might want to try using a standard for iterator which allows access to the position (used by std::vector::erase)
int len=finalSuggestions.size();
for (int i=0, ; i<len; ++i) {
// Save a copy of the item
auto item = finalSuggestions.at(i);
if (double((item.num)/(topword.num)) < 50) {
// Erase the item in the list
finalSuggestions.erase(i);
// Add the copy of the item back in at the front
finalSuggestions.insert(finalSuggestions.begin(), item);
break;
}
}
... or using a std::iterator ...
for (auto it = finalSuggestions.begin(); it != finalSuggestions.end(); ++it) {
if (double((*it->num)/(topword.num)) < 50) {
// Save a copy of the item
auto item = *it;
// Erase the item in the list
finalSuggestions.erase(it);
// Add the copy of the item back in at the front
finalSuggestions.insert(finalSuggestions.begin(), item);
break;
}
}
std::vector objects use contiguous memory for their elements, which means actually moving memory around during altering of the container. If you are going to be moving elements around you may want to look into std::list or std:deque. The definition of these containers are nearly identical (read: drop in replacements) to each other making it fairly straight-forward to replace them.
Suggestion:
The std::deque is designed for optimal insertions at both the beginning and the end of the container. Taken from the site cplusplus.com:
... they provide a functionality similar to vectors, but with efficient insertion and deletion of elements also at the beginning of the sequence, and not only at its end. But, unlike vectors, deques are not guaranteed to store all its elements in contiguous storage locations: ...
Anton's answer is correct. However if you do this sort of thing a lot you should consider a different data structure. Both the erase and the insert are O(N) operations, where N is the size of the vector. A list would be better if this is a common operation.
It is functionally equivalent to Anton's answer, but I would use std::find_if to get the an iterator to the element you are looking for instead of a loop.
//add #include <algorithm> to your source file
auto result = std::find_if(finalSuggestions.begin(), finalSuggestions.end(), condition_func);
if(result != finalSuggestions.end())
{
auto resultValue = *result;
finalSuggestions.erase(result);
finalSuggestions.insert(finalSuggestions.begin(), resultValue);
}
condition_func should be a function returning bool that takes a parameter matching the type of the elements in your vector (in this case, Suggestion):
bool condition_func(Suggestion elementValue) { /*condition here*/ }
More information on find_if is available here.
Maybe using std::iter_swap could solve your problem.
#include <iostream>
#include <algorithm>
#include <vector>
using namespace std;
int main () {
vector<int> myvector{};
for(int io{}; io<7; ++io) myvector.push_back(io+1);
for (std::vector<int>::iterator it=myvector.begin(); it!=myvector.end(); ++it)
cout << ' ' << *it;
cout << '\n';
iter_swap(myvector.begin(),myvector.begin()+2);//exchange the third element with the first.
cout << "myvector contains:";
for (std::vector<int>::iterator it=myvector.begin(); it!=myvector.end(); ++it)
std::cout << ' ' << *it;
std::cout << '\n';
return 0;
}