Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How can I search an std::map using a key of a different type

If I have std::map<X, Blah>, what is the best way of looking up a matching item in the map using an instance of Y?

Assume the information in Y is enough to uniquely find an X, but for performance reasons I don't want to create an instance of X by copying Y values.

I realize I can do this by creating a common base class or interface for X and Y and making that the map key, but is there any other way? e.g. creating some sort of comparator object?

Here is the sample code for clarity:

class X
{
public:
    int id;
    int subId;
};

std::map<X, Details> detailsMap;

class Y
{
public:
    int getId();
    int getSubId();
    int someOtherUnrelatedThings1;
    int someOtherUnrelatedThings2;
};

Now, if I have an instance of Y, in principle I should be able to find matching items in my map, given I can get an id and subId pair. But can I do it without creating an instance of X and copying over the id and subId?

like image 870
nappyfalcon Avatar asked Aug 10 '15 15:08

nappyfalcon


People also ask

How do I search for something on a map C++?

C++ map find() function is used to find an element with the given key value k. If it finds the element then it returns an iterator pointing to the element. Otherwise, it returns an iterator pointing to the end of the map, i.e., map::end().

Is std::map sorted by key?

std::map is a sorted associative container that contains key-value pairs with unique keys. Keys are sorted by using the comparison function Compare . Search, removal, and insertion operations have logarithmic complexity.

How do I find a key on a map?

find() is used to search for the key-value pair and accepts the “key” in its argument to find it. This function returns the pointer to the element if the element is found, else it returns the pointer pointing to the last position of map i.e “map. end()” .

Can maps pair with keys?

So, you can use pair as a key in a map as follows: map<pair<int,string> , long> mp; mp. insert(make_pair(make_pair(2,"me"),123456789);


1 Answers

With C++14 you can use heterogeneous lookup.

If you'd like to find an element with key that compares equivalent to the argument of std::map::find, you should provide a Comparator as a third template parameter which should have Comparator::is_transparent denoted as a type. It should also contain bool operator() comparing your map key with any other type you'd like.

Funny description aside, here's an example:

struct X
{
    int id;
    int subid;
};

struct Details {};

struct Comparator
{
    using is_transparent = std::true_type;

    // standard comparison (between two instances of X)
    bool operator()(const X& lhs, const X& rhs) const { return lhs.id < rhs.id; }

    // comparison via id (compares X with integer)
    bool operator()(const X& lhs, int rhs) const { return lhs.id < rhs; }
    bool operator()(int lhs, const X& rhs) const { return lhs < rhs.id; }

    // Same thing with Y
    bool operator()(const X& lhs, const Y& rhs) const { return lhs.id < rhs.getId(); }
    bool operator()(const Y& lhs, const X& rhs) const { return lhs.getId() < rhs.id; }
};

int main()
{
    std::map<X, Details, Comparator> detailsMap = {
        { X{1, 2}, Details{} }, 
        { X{3, 4}, Details{} },
        { X{5, 6}, Details{} }
    };

    // it1 and it2 point to the same element.
    auto it1 = detailsMap.find(X{1, 2});
    auto it2 = detailsMap.find(1);

    std::cout << detailsMap.size() << std::endl;
    std::cout << std::boolalpha << (it1 == detailsMap.end()) << std::endl; // false
    std::cout << std::boolalpha << (it1 == it2) << std::endl; // true
}

Note however that GCC didin't implement it until revision 219888.

like image 130
Michał Góral Avatar answered Oct 06 '22 01:10

Michał Góral