c++referencestdlist

remove element from std::list by reference


std::list<Reader> readers;

readers.push_back(Reader());

Reader& r = *(readers.begin());

/* at this point, the exact place in list, where the reader was picked out, is forgotten. 
   Only 'r' shows which element of the list it is. */

readers.erase(r); //<---how to do this?

Clients get the new instances 'reader' objects from a manager/dispatcher. The manager maintains an internal list of whatever was dispatched and invalidates/frees up a cached data if "everyone interested" picked it up by observing the pool of readers dispatched.

When the client is no longer interested in the data, it should return the reader to the manager for removal from the pool. But I don't want the client to keep an iterator - it's absolutely uninterested in guts of the manager and the pool of the readers; only needs this one own reader it got, not an iterator pointing to it. So, for deletion, it calls the manager's cleanup function, with the reference to that single reader.

Is there a nicer way to erase that reader from the list than to iterate through the whole list in search of that one reader the reference leads to?


Solution

  • Your options if you only have a reference to the object is to use std::list::remove

    readers.remove(r);
    

    or std::find in conjunction with std::list::erase

    readers.erase(std::find(readers.begin(), readers.end(), r));
    

    The former has to iterate the entire list while the latter will stop when it finds the first element and then removes it. For large list this can make a big difference.

    Both of these options only work when the items are unique. If you have non unique elements then you can use std::find_if and provide a functor that compares the address of the items. That way you can guarantee you only delete the object the reference actually refers to instead of compares equal to.

    if (auto it = std::find_if(readers.begin(), readers.end(), [&](const auto& e) {return &r == &e;}; it != readers.end())
        readers.erase();