Search code examples
c++stdlist

Construct one list from element of another list without adding to heap memory


I am developing a memory critical application. I first generate a list, for example (C++11):

std::list<string> nodes({"Hello","Welcome", "Hi", "World"});

I need to now create a smaller list with the second and third elements of 'nodes.' Naively, I would do:

std::list<string> sub_nodes;
sub_nodes.push_back(*std::next(nodes.begin(),1));
sub_nodes.push_back(*std::next(nodes.begin(),2));

But this clearly allocates memory in the heap for sub_nodes by allocating new memory.

*(sub_nodes.begin()) = "NotWelcome"; //changing first element
std::list<string>::iterator it;
for(it=nodes.begin();it!=nodes.end();++it) cout<<*it<<'\t';
cout<<'\n';
//Hello Welcome Hi World
for(it=sub_nodes.begin();it!=sub_nodes.end();++it)
cout<<*it<<'\t';
cout<<'\n';
//NotWelcome Hi

What I wish to accomplish is to have the elements of sub_nodes occupy the same address of the elements of nodes from which they were created. In other words, I want the changes made to elements of sub_nodes reflected in those elements in nodes and vice versa. In C linked lists, this would be straight forward since the list nodes are basically pointers. How would I accomplish the same in C++?


Solution

  • You can use std::reference_wrapper, which works as a reference and is able to be stored in a container.

    std::list<std::reference_wrapper<std::string>> sub_nodes; // #include <functional>
    

    Note although std::reference_wrapper has an implicit conversion operator to its value type, sometimes you still have to use its explicit get method. For example, you have to use

    (*sub_nodes.begin()).get() = "NotWelcome"; 
    

    to change the referenced object.

    The following is a full working example:

    #include <iostream>
    #include <functional>
    #include <list>
    #include <string>
    
    int main()
    {
        std::list<std::string> nodes({"Hello","Welcome", "Hi", "World"});
        std::list<std::reference_wrapper<std::string>> sub_nodes;
        sub_nodes.push_back(*std::next(nodes.begin(), 1));
        sub_nodes.push_back(*std::next(nodes.begin(), 2));
        (*sub_nodes.begin()).get() = "NotWelcome"; 
        for (auto it = nodes.begin(); it != nodes.end(); ++it) std::cout << *it << '\t';
        std::cout << '\n';
        // Hello NotWelcome Hi World
        for (auto it = sub_nodes.begin(); it != sub_nodes.end(); ++it)
            std::cout << (*it).get() << '\t';
        std::cout << '\n';
        // NotWelcome Hi
    }