Can I extend std::map::lower_bound to search on non-key_type arguments?
Asked Answered
S

1

1

Here is an illustration of my situation. I have a std::map and I want to find the first pair<key,value> where the key is any member of an equivalence class of keys.

#include <map>

struct Category
{
    int foo;
    int bar;

    bool operator < (const Category & rhs) const;    
    bool operator > (const Category & rhs) const;
};

struct Key
{
    Category category;
    float quality;

    bool operator < (const Key & rhs) const
    {
        if (category < rhs.category)
            return true;
        else if (category > rhs.category)
            return false;
        else
            return quality < rhs.quality;
    }
};

struct Value {};

typedef std::map <Key, Value> Container;

Container::iterator find_low_quality
(
    Container & container,
    const Category & category
)
{
    return container.lower_bound (category);
}

Container::iterator find_high_quality
(
    Container & container,
    const Category & category
)
{
    // some checks need to be done, here omitted for brevity
    return --container.upper_bound (category);
}

This doesn't work because map::lower_bound and map::upper_bound only take a key_type (i.e. Key) argument. I couldn't get std::lower_bound to compile, I see it expects a LegacyForwardIterator but I'm having a hard time interpreting the spec for this.

Insofar as the Key for my map is ordered, the Key has a compatible ordering with Category, viz: k<c if and only if k.category<c, so my requirements seem to make logical sense.

In the real situation, the Key class is more complex, and separating the quality/category components (in order to use a map<category,map<quality,value>> solution) isn't really going to work, in case that's what you're thinking of.

How can I find the lower (and upper) bounds of the range of elements in my map whose keys are equivalent to some non-key value?

South answered 5/1, 2019 at 12:56 Comment(0)
A
4

C++14 introduced the concept of a transparent comparator, where it is possible to use find, lower_bound, upper_bound, ... with anything that can be compared to the key type, as long as the comparator explicitly opts in to this behavior.

In your case you'd need to add a custom comparator

struct KeyComparator {
    // opt into being transparent comparator
    using is_transparent = void;

    bool operator()(Key const& lhs, Key const& rhs) const {
        return lhs < rhs;
    }

    bool operator()(Key const& lhs, Category const& rhs) const {
      return lhs.category < rhs;
    }

    bool operator()(Category const& lhs, Key const& rhs) const {
      return lhs < rhs.category;
    }
};

and then you need to use that in your Container

typedef std::map <Key, Value, KeyComparator> Container;

Live demo

Armada answered 5/1, 2019 at 13:45 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.