SF.
SF.

Reputation: 14039

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?

Upvotes: 3

Views: 5272

Answers (4)

NathanOliver
NathanOliver

Reputation: 180490

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();

Upvotes: 4

Sly_TheKing
Sly_TheKing

Reputation: 528

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.

Upvotes: 4

apple apple
apple apple

Reputation: 10591

you can compare the pointers to check if they are same object

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

Upvotes: 4

Vlad from Moscow
Vlad from Moscow

Reputation: 310940

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

Upvotes: 1

Related Questions