简体   繁体   中英

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?

Use std::remove in combination with erase

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

Also, u can't delete element from list by value, without iterating it. If you think about it, it doesn't even make sense, because pointers inside the list have to be updated.

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.

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

您可以比较指针以检查它们是否是同一个对象

readers.remove_if([r=&r](auto& x){return &x==r;});

If the list can contain equal values then you can do something like the following

#include <iostream>
#include <list>

int main()
{
    struct Reader { std::pair<char, int> p; };
    std::list<Reader> readers;

    readers.push_back({{ 'A', 1 } });
    readers.push_back({ { 'A', 2 } });
    Reader &rr = readers.back();
    readers.push_back({ { 'A', 3 } });

    readers.remove_if([&rr](const Reader &r) { return &r == &rr; });

    for (const auto &r : readers)
    {
        std::cout << r.p.first << ' ' << r.p.second << std::endl;
    }

    return 0;
}

The program output is

A 1
A 3

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