Search code examples
c++stdvectorstdlist

List/Vector iterator wont change const reference attribute in a derived class


new to c++. I dont know (cant find the right search terms) why the following happens.

Basically the Parent class has a member inputs. The Child class has a const reference to Parent::inputs and a Child::set_inputs() method to alter Parent::inputs. When I have a std::list<Child> or std::vector<Child> to which i iterate through and attempt to set_inputs, Parent::inputs changes but Child::input does not. Whats happening behind the scenes and how would i work my way through this? as I would need to work with lists/vectors.

class Parent {

public:
    Parent() = default;
protected:
    double inputs;
};

class Child : public Parent {

public:

    Child() = default;

    void set_inputs(const double& X) {
        inputs = X;
    }

public:
    const double& input = inputs;
};

int main() {
    using std::cout;
    using std::endl;

    // Without Lists, works outputs '3.0'
    double X1 = 3.0;
    Child test0;
    test0.set_inputs(X1);
    cout << test0.input << endl;


    // Neither Lists or Vectors work, does not output '3.0'
    Child test1, test2, test3;
    std::list<Child> test_lists = { test1, test2, test3 };
    for (std::list<Child>::iterator t = test_lists.begin(); t != test_lists.end(); ++t) {
        t->set_inputs(X1);
        cout << t->input << endl;
    }
}


Solution

  • The default copy-ctor is establishing the reference input from one instance to another. You're making copies of your child objects when you put them in your container. In other words, the input members of your container copies still reference the inputs members of you test1, test, etc. instances. Therefore when calling set_inputs to change the instances Parent::inputs member (which is happening), it has nothing to do with the inputs that is being referenced by input in those contained Child objects. Note: this is also a recipe for a dangling reference just waiting in the wings, if test1 etc go out of scope before your container.

    Put another way, the default copy ctor is doing this:

    Child(const Child& c) : Parent(c), input(c.input) {}
    

    Ouch. now that input member is referencing whatever c.input referenced; not Parent::inputs .

    To see what happens without copying change the decl you can do this: std::list<Child> test_lists(3); . Now when you run your program it will run "correctly". There are no copies being made, therefore no mis-wires of the input reference member.

    To address this, you need to make sure that the input member is always pinned to its parent inputs, including on copy-ctor. In short, add this:

    Child(const Child& c) : Parent(c), input(inputs) {}

    to the Child class.