As the title says, I want to add an element to a std::vector in certain cases while iterating through the vector. With the following code, I'm getting an error "Debug assertion failed". Is it possible to achieve what I want to do?
This is the code I have tested:
#include <vector>
class MyClass
{
public:
MyClass(char t_name)
{
name = t_name;
}
~MyClass()
{
}
char name;
};
int main()
{
std::vector<MyClass> myVector;
myVector.push_back(MyClass('1'));
myVector.push_back(MyClass('2'));
myVector.push_back(MyClass('3'));
for each (MyClass t_class in myVector)
{
if (t_class.name == '2')
myVector.push_back(MyClass('4'));
}
return 0;
}
EDIT:
Well, I thought for each was standard C++, but it seems that it's a Visual Studio feature:
for each, in
Visual c++ "for each" portability
The act of adding or removing an item from a std::vector invalidates existing iterators. So you cannot use any kind of loop that relies on iterators, such as for each, in, range-based for, std::for_each(), etc. You will have to loop using indexes instead, eg:
int main()
{
std::vector<MyClass> myVector;
myVector.push_back('1');
myVector.push_back('2');
myVector.push_back('3');
std::vector<MyClass>::size_type size = myVector.size();
for (std::vector<MyClass>::size_type i = 0; i < size; ++i)
{
if (myVector[i].name == '2')
{
myVector.push_back('4');
++size; // <-- remove this if you want to stop when you reach the new items
}
}
return 0;
}
As pointed out by pyon, inserting elements into a vector while iterating over it (via iterators) doesnt work, because iterators get invalidated by inserting elements. However, it seems like you only want to push elements at the back of the vector. This can be done without using iterators but you should be careful with the stop condition:
std::vector<MyClass> myVector;
size_t old_size = myVector.size();
for (int i=0;i<old_size;i++) {
if (myVector[i].name == '2') { myVector.push_back(MyClass('4')); }
}
After following the previous answers, you can use const auto& or auto& to have clean code. Should be optimized in release build by the compiler.
std::vector<MyClass> myVector;
std::vector<MyClass>::size_type size = myVector.size();
for (std::vector<MyClass>::size_type i = 0; i < size; ++i)
{
const auto& element = myVector[i];
element.do_stuff();
}
Related
I have a program where I have to delete some entries of a vector of structs. Im doing it like this
for(int i=0; i<molec.size(); i++)
{
if(!molec[i].mine)
molec.erase(molec.begin()+i);
}
molec.mine is a boolean defined in the struct. The problem is that when I do this, it erases exactly half of the elements with molec.mine=false. I tried to search for some alternatives and found that I could do it with
vector.erase(std::remove(vec.begin(),vec.end(),1963),vec.end());
The thing with this is that I don't know how to do it with a vector of structs.
How can I solve this problem?
You are probably looking for std::remove_if. As in
molec.erase(
std::remove_if(
molec.begin(), molec.end(),
[](const auto& elem) { return !elem.mine; }),
molec.end());
i++ shouldn't be performed when element is erased. e.g.
for(int i=0; i<molec.size(); )
{
if(!molec[i].mine)
molec.erase(molec.begin()+i);
else
i++;
}
And yes, you should use algorithm library to avoid such issues.
molec.erase(std::remove_if(molec.begin(),molec.end(),[](const auto& v) { return !v.mine;}),molec.end());
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:
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.
I have this loop
for(int i=0;i<vec1.size();++i)
{
if(vec1[i]==*p)
{
vec1[i]=*p;
cout<<"element updated"<<endl;
}
else
{
cout<<"push_back"<<endl;
vec1.push_back(*p);
}
}
I'm inserting objects in container class and I've overloaded the == to check two parameters inside the object and if they match I want to update the them and if they don't match I want to put them in the vector, but I don't seem to be able to properly populate my vector, when I do vec1.size() I get 0 even when I insert 3 objects.
You're problem is that your if is inside your search loop. Your if will never be executed, because your loop body never runs, because your .size() will never be greater than 0.
Try this:
// UNTESTED
std::vector<person> vec1;
add(person *p) {
std::vector<person>::iterator it = std::find(vec1.begin(), vec1.end(), *p);
if(it == vec1.end())
vec1.push_back(*p);
else
*it = *p;
}
Or, if you really want to code the loop by hand:
// UNTESTED
std::vector<person> vec1;
add(person *p) {
int i;
for(i=0;i<vec1.size();++i) {
if(vec1[i] == *p)
break;
}
if(i == vec1.size())
vec1.push_back(*p);
else
vec1[i] = *p;
}
Of course, you might consider changing your container. Using a std::map would shorten your code and reduce the time it takes to manipulate large data sets.
std::map<std::string, person> map1;
add(person *p) {
map1[p->name] = *p;
}
When the vec1 starts from empty, the for loop is not going to run. So you want to have at least one element in vec1 to start with. How about add this:
vec1.push_back(*p);
for(int i=0;i<vec1.size();++i){//the rest}
I need to implement a really simple LRU cache which stores memory addresses.
The count of these addresses is fixed (at runtime).
I'm only interested in the last-recently used address (I don't care about the order of the other elements).
Each address has a corresponding index number (simple integer) which isn't unique and can change.
The implementation needs to run with as less overhead as possible. In addition to each address, there's is also a related info structure (which contains the index).
My current approach is using a std::list to store the address/info pair and a boost::unordered_multimap which is a mapping between the index and the related iterator of the list.
The following example has nothing to do with my production code. Please note, that this is just for a better understanding.
struct address_info
{
address_info() : i(-1) {}
int i;
// more ...
};
int main()
{
int const MAX_ADDR_COUNT = 10,
MAX_ADDR_SIZE = 64;
char** s = new char*[MAX_ADDR_COUNT];
address_info* info = new address_info[MAX_ADDR_COUNT]();
for (int i = 0; i < MAX_ADDR_COUNT; ++i)
s[i] = new char[MAX_ADDR_SIZE]();
typedef boost::unordered_multimap<int, std::list<std::pair<address_info, char*>>::const_iterator> index_address_map;
std::list<std::pair<address_info, char*>> list(MAX_ADDR_COUNT);
index_address_map map;
{
int i = 0;
for (std::list<std::pair<address_info, char*>>::iterator iter = list.begin(); i != MAX_ADDR_COUNT; ++i, ++iter)
*iter = std::make_pair(info[i], s[i]);
}
// usage example:
// try to find address_info 4
index_address_map::const_iterator iter = map.find(4);
if (iter == map.end())
{
std::pair<address_info, char*>& lru = list.back();
if (lru.first.i != -1)
map.erase(lru.first.i);
lru.first.i = 4;
list.splice(list.begin(), list, boost::prior(list.end()));
map.insert(std::make_pair(4, list.begin()));
}
else
list.splice(list.begin(), list, iter->second);
for (int i = 0; i < MAX_ADDR_COUNT; ++i)
delete[] s[i];
delete[] info;
delete[] s;
return 0;
}
The usual recommendation is to dig up Boost.MultiIndex for the task:
index 0: order of insertion
index 1: key of the element (either binary search or hash)
It's even demonstrated on Boost site if I recall correctly.