Nubcake
Nubcake

Reputation: 469

std::multiset define comparator for insertion and comparison

I'm using a std::multiset of pointers to objects to implement Z-ordering in my game, so I don't need to sort the structure on each insertion. I use a comparator for insertion by the object's depth:

struct rendererComparator
{
    bool operator ()(const Renderable* r1, const Renderable* r2) const
    {
        return r1->depth < r2->depth;
    }
};

std::multiset<Renderable*, rendererComparator> m_Renderables;

However when it comes to erasing an element in the multiset, the call to erase removes all elements which have the same depth which is undesirable. I tried the suggestions in this question: In std::multiset is there a function or algorithm to erase just one sample (unicate or duplicate) if an element is found but

auto iterator = m_Renderables.find(renderable);
if (iterator != m_Renderables.end())
{
    m_Renderables.erase(renderable);
}

Still erases all the elements with the same depth because of the comparator.

Is it possible to define 2 comparators for std::multiset without boost? (How can I set two kind of comparator (one for insert, one for find) on this multiset?) One for insertion and one for comparison?

Thanks

Edit: Jignatious pointed out that I wasn't erasing the iterator (typo by me). I solved it by using std::find_if

auto iterator = std::find_if(m_Renderables.begin(), m_Renderables.end(), [renderable](const Renderable* r1) { return r1 == renderable; });
if (iterator != m_Renderables.end())
{
    m_Renderables.erase(iterator);
}

Upvotes: 1

Views: 579

Answers (2)

Vitalik Beloded
Vitalik Beloded

Reputation: 1

Instead of multiset, you can use std::set with comparer like this:

struct Element
{
    int value;

    Element(int v)
    {
        value = v;
    }

    bool operator() (Element* const& left, Element* const& right) const
    {
        if (left->value == right->value)
            return (left < right);

        return left->value < right->value;
    }
};

It will store multiple values like multimap, but without 'erase all' on erase, without replace same values on insert and with proper find by reference.


std::set<Element*, Element> set;
set.insert(new Element(10));
auto last = new Element(10);
set.insert(last); // 10 10 like in multiset
set.erase(last); // will delete proper references

Upvotes: 0

jignatius
jignatius

Reputation: 6484

The problem is on this line:

m_Renderables.erase(renderable);

which erases all elements with the same value.

You need to erase with the iterator from the find() function call instead. That will erase the single element that the iterator points to:

m_Renderables.erase(iterator);

Note that std::multiset::find() returns an iterator pointing to the lower bound (or first) of the elements which is searched in the multiset if it exists, otherwise the one past the end element iterator.

Upvotes: 2

Related Questions