简体   繁体   中英

Changing values of objects in std vector c++

I noticed that I am not able to change values of objects stored inside a std::vector or a std::map . The output of the code below gives "0" as a result of the toggle() function call in both cases. This behavior is not present while using the object directly, outside of the vector. Am I missing something about std::vector or similar containers? How do we properly store objects in containers and keep the ability to change their values?

#include <iostream>
#include <functional>
#include <vector>

class OtherThing{
public:
void addElement(std::function<int()> func){
    this->myfuncs.push_back(func);
}
void toggle(){
    std::cout << "toggle .. " << this->myfuncs[0]() << std::endl;
}
private:
std::vector<std::function<int()>> myfuncs; 
};

class Thing: public OtherThing{
public:
Thing(){
    std::function<int()> myfunc= [this]()->int{
        std::cout << "from lambda " << this->value << std::endl;
        return this->value;
        };
    this->addElement(myfunc);
    }
void setValue(int value){
    this->value = value;
}
int getValue(){
    return this->value;
}
private:
int value;
};


int main() {
    // container for things 
    std::vector<Thing> mythings;
    // a thing
    Thing a;
    
    mythings.push_back(a);
    
    mythings[0].setValue(666);
    
    mythings[0].toggle();
    mythings[0].setValue(999);
    mythings[0].toggle();
    return 0;
}

output:

clang++-7 -pthread -std=c++17 -o main main.cpp
./main
toggle .. from lambda 0
0
toggle .. from lambda 0
0

push_back(a) makes a copy of a . mythings[0] returns a reference to that copy. As such, anything done to modify the members of mythings[0] will not be reflected in a , and vice versa.

However, when a is copied by push_back() , Thing 's compiler-generated copy constructor will copy the myfuncs vector as-is, and so the lambda that a 's constructor had stored in that vector is also copied as-is. That lambda had captured a this pointer that points at a , not the new copy.

So, when you call mythings[0].setValue() , you are modifying the value of the copied Thing , but when you call mythings[0].toggle() , you are calling a lambda that prints the value of a - which was never initialized in the first place , so the output is indeterminate until a.setValue() is called.

To fix this, you need to implement your own copy constructor for Thing that stores its own lambda capturing the copied Thing 's this pointer, not store a copy of the earlier lambda from the Thing being copied from, eg:

Thing(const Thing &src){
    value = src.value;
    std::function<int()> myfunc = [this]()->int{
        std::cout << "from copied lambda " << this->value << std::endl;
        return this->value;
    };
    this->addElement(myfunc);
}

Online Demo

I was trying this also based on the comments. Honestly, I do not like this. Too much script just to call toggle()

#include <iostream>
#include <functional>
#include <vector>

class Thing;

class OtherThing{
public:
void addElement(std::function<int(Thing*)> func){
    this->myfuncs.push_back(func);
}
void toggle(Thing* obj){
    std::cout << "toggle .. " << this->myfuncs[0](obj) << std::endl;
}
private:
std::vector<std::function<int(Thing*)>> myfuncs; 
};

class Thing: public OtherThing{
public:
Thing(){
    std::function<int(Thing*)> myfunc= [](Thing* obj)->int{
        std::cout << "from lambda " << obj->value << std::endl;
        return obj->value;
        };
    this->addElement(myfunc);
    }
void setValue(int value){
    this->value = value;
}
int getValue(){
    return this->value;
}
private:
int value;
};


int main() {
    // container for things 
    std::vector<Thing> mythings;
    
    // a thing
    Thing a;
    
    
    // copy is here
    mythings.push_back(a);
    
    // test
    mythings[0].setValue(666);
    mythings[0].toggle(&mythings[0]);
    mythings[0].setValue(999);
    mythings[0].toggle(&mythings[0]);

    return 0;
}

output:

toggle .. from lambda 666
666
toggle .. from lambda 999
999

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM