c++reference

How to manage std::list elements as references?


I have the following code:

struct Foo {
    int var1;
    int var2;


    friend std::ostream& operator<<(std::ostream& os, const Foo& s){
        return os << "[Foo] " << s.var1 << "," <<  s.var2 ;
    }
};

int main() {
    Foo foo;
    foo.var1 = 1;
    foo.var2 = 2;
    std::list<Foo> list;
    list.push_back(foo);

    Foo &foo2 = list.front();
    foo2.var2 = 5;

    std::cout << "foo (" << &foo << "): " << foo << std::endl;
    std::cout << "foo2 (foo from list) (" << &list.front() << "): " << foo2 << std::endl;
}

I want both foo and foo2 to reference the same object. So when I assign 5 to foo2.var2, I would want to modify foo.var2 as well. Yet, as we can see in the following output this is not happening:

foo (0x7fffffffe140): [Foo] 1,2
foo2 (foo from list) (0x61ac30): [Foo] 1,5 

What would be the correct way to do that?


Solution

  • When you use push_back to insert elements into a list, push_back creates a copy which is inserted into the list. A solution is to use a std::reference_wrapper instead as the underlying type of the list, like

    std::list<std::reference_wrapper<Foo>> lst;
    

    and then push into it like

    lst.push_back(foo);
    

    Here is a super simple example that shows you how it works:

    #include <functional>
    #include <iostream>
    #include <list>
    
    int main() 
    {
        int i = 42;
        std::list<std::reference_wrapper<int>> lst;
    
        lst.push_back(i);           // we add a "reference" into the list
        lst.front().get() = 10;     // we update the list
        std::cout << i;             // the initial i was modified!
    }
    

    Live on Coliru

    You need the reference_wrapper since you cannot simply create a list of references, like std::list<Foo&>. Alternatively, you can use pointers, but I find the reference_wrapper approach more transparent.

    In the simple example above note the need to use std::reference_wrapper::get() to obtain the underlying reference, as the reference_wrapper is on the left hand side of the assignment operator and hence won't be implicitly converted to int via std::reference_wrapper::operator T&.

    Below is your full working code modified to use reference_wrappers:

    http://coliru.stacked-crooked.com/a/fb1fd67996d6e5e9