Changing an element within a vector of structures - c++

I'm iterating through a set of elements within a vector of structures and want to change an element in one of the structures. When I write to the element to change the value, the update isn't retained. Here is what I have:
first, in a header file:
std::vector<Sched::ScheduledEvent_t> v_SchedEvents;
typedef std::vector<Sched::ScheduledEvent_t>::const_iterator event_iter;
then later in a .cpp module...
for (event_iter i = v_SchedEvents.begin(); i != v_SchedEvents.end(); ++i)
{
ScheduledEvent_t event = *i;
if(event.member == true) {
event.member = false;
}
}
The value of event.member for the given structure in the vector isn't staying false. When returning to this loop, the conditional statement is run again.
Could it have anything to do with the typedef for the iterator?

Two problems here.
1) You're making a copy:
ScheduledEvent_t event = *i;
event is a copy of the element in the vector. Modifing event won't affect it.
2) You are using a const_iterator which only allows reading the value, not changing it.
Use a iterator instead
typedef std::vector<Sched::ScheduledEvent_t>::iterator event_iter;
and use it directly:
if (i->member) { // == true useless
i->member = false;
}
Or a for-range loop if you have access to C++11 or more recent:
for (auto & event : v_SchedEvents) {
if (event.member) {
event.member = false;
}
}

The const_iterator prevents the modification of the referenced value.
iterator : Behaves like value_type*.
const_iterator : Behaves like const_value_type*
vector<node> v;
v.push_back(node(10, 11));
for( std::vector<node>::const_iterator it = v.begin() ; it != v.end() ; ++it ){
node n = *it;
n.member = 12; //A local copy of node `*it`. So its ok to modify.
n = node(10, 13); //Okay since local Copy is not const
//(*it).member = 12; //error assignment of member 'node::a' in read-only object
//*it = node(10, 13); //error passing 'const node' as 'this' argument discards qualifiers
}
for( std::vector<node>::iterator it = v.begin() ; it != v.end() ; ++it ){
//cout << *it.a;
node n = *it;
n.a = 12;
n = node(10, 13); //No problem
(*it).a = 12; //No problem
*it = node(10, 13); //No problem
}
You are able to modify member value because you are getting a local copy of const_iterator referenced value by doing ScheduledEvent_t event = *i;

It is a simple matter of reference type and local variable.
When You do
ScheduledEvent_t event = *i;
You are creating a simple local variable "event" and copying everything from the source where "i" is pointing to.
So, if you wish to change the element in the structure change your code from
event.member = false;
to
*i.member = false;
or (my preference)
i->member = false;
I hope that helps.

Related

Unable to remove all object from vector

so basically i have a vector of objects called "Things" that I populate over the console. These are the name of the object I populate them with (tom, coin, coin, bomb).
for (int i = 0; i < 5; i++){
for (Thing * t : *locations[i]->getThings()) {
if (t->getName().compare("bomb") == 0){
for (Thing * all : *locations[i]->getThings()){
if(all->getName().compare("tom") != 0){
locations[i]->remove(all);
}
}
}
}
}
This code is run every time to check if there is a "thing" object called bomb in the list and would remove every other object other than tom.
So from the populated example above, the expected list should just be {tom}. However when the code runs it is {tom, coin} which means it fails to delete the other "non-tom" object
The problem is that you are modify the Things vector while you are still iterating through it, so you are going to be invalidating the iterators that the for loops are using, which is undefined behavior.
You can do something more like this instead, using the Erase-Remove idiom:
const auto is_bomb = [](const Thing *t){ return t->getName() == "bomb"; };
const auto is_not_tom = [](const Thing *t){ return t->getName() != "tom"; };
...
auto *things = locations[i]->getThings();
if (std::any_of(things->begin(), things->end(), is_bomb)){
things.erase(
std::remove_if(things->begin(), things->end(), is_not_tom),
things->end()
);
}
Or, if you are using C++20 or later:
const auto is_bomb = [](const Thing *t){ return t->getName() == "bomb"; };
const auto is_not_tom = [](const Thing *t){ return t->getName() != "tom"; };
...
auto *things = locations[i]->getThings();
if (std::any_of(things->begin(), things->end(), is_bomb)){
std::erase_if(*things, is_not_tom);
}

C++: I get an iterator in one method, how to modify the original list by iterator in another mothod?

I have a list, I am tring to find a list item by value in one method, in another method to modify this item.
Here is the simplified code:
using namespace std;
//find iterator by value
list<int>::iterator get_iterator(list<int> li,int value) {
list<int>::iterator it;
for(it = li.begin();it != li.end();it++) {
if(*it == value) {
return it;
}
}
return li.end();
}
int main() {
list<int> num_list;
for(int i = 0;i < 20;i++) {
num_list.push_back(i);
}
// print the list
for(list<int>::iterator it = num_list.begin();it != num_list.end();it++) {
cout << *it << endl;
}
list<int>::iterator target_it = get_iterator(num_list,10);
// cout << *target_it <<endl;
*target_it = 100; // try to modify the original list(num_list) by iterator.
// print the list again
for(list<int>::iterator it = num_list.begin();it != num_list.end();it++) {
cout << *it << endl;
}
}
The original list has not been modified, is there any chance to make it?
Thanks in advance
list<int>::iterator get_iterator(list<int> li,int value) gets the list by value, so it is a copy of the original list.
After returning the iterator, the copy list gets destroyed and so the iterator is invalid. Using it invokes undefined behaviour.
You want to pass the list by reference
list<int>::iterator get_iterator(list<int> &li,int value)
//^^
Your idea is actually right. However your issue comes from the function get_iterator. In your current implementation, you're taking the parameter li by value, which internally create a copy. Thus the iterator you get belong to a copy of the list in the function main, and not from that list.
You can fix your issue by taking the list by reference by replacing the type of parameter li from list<int> li to list<int> &li.
You can either make it a global variable or pass it to the function by reference (prefixing the variable name with &)

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;
}

Updating std::map object data

I have a std::map object as follows
typedef std::map<int,int> RoutingTable;
RoutingTable rtable;
and then I've initialized it in a function
for (int i=0; i<getNumNodes(); i++)
{
int gateIndex = parentModuleGate->getIndex();
int address = topo->getNode(i)->getModule()->par("address");
rtable[address] = gateIndex;
}
Now I want to change the values in the rtable in another function. how can I achieve this?
Pass the rtable by reference:
void some_func(std::map<int, int>& a_rtable)
{
// Either iterate over each entry in the map and
// perform some modification to its values.
for (std::map<int, int>::iterator i = a_rtable.begin();
i != a_rtable.end();
i++)
{
i->second = ...;
}
// Or just directly access the necessary values for
// modification.
a_rtable[0] = ...; // Note this would add entry for '0' if one
// did not exist so you could use
// std::map::find() (or similar) to avoid new
// entry creation.
std::map<int, int>::iterator i = a_rtable.find(5);
if (i != a_rtable.end())
{
i->second = ...;
}
}