Boost unordered_flat_map
is pretty good, it's way faster than std::unordered_map
. I'm trying to use it with heterogenous key types:
boost::unordered::unordered_flat_map<std::string, int> my_map;
my_map.emplace(std::string("Hello"), 1);
my_map.find(std::string_view("Hello"));
// no instance of overloaded function matches the argument list
argument types are: (std::string_view)
Now, because I created a map that has key type std::string there is a find function that takes a std::string, but there's also a templated find function that takes a K, and usually these things are supposed to work provided the container:
- Knows how to hash the heterogenous type, and
- Knows of a equality operator that takes the heterogenous type as the right hand side.
Even if I do:
auto equal_to = [](auto& lhs, auto& rhs) { return lhs == rhs; };
And pass this equality class to the container I still get the error:
boost::unordered::unordered_flat_map<std::string, int, decltype(equal_to)> my_map;
How am I supposed to get this to work?
From cppreference we learn that transparent hash/equality enable heterogenous key lookup:
3,4) Finds an element with key that compares equivalent to the value x. This overload participates in overload resolution only if Hash::is_transparent and KeyEqual::is_transparent are valid and each denotes a type. This assumes that such Hash is callable with both K and Key type, and that the KeyEqual is transparent, which, together, allows calling this function without constructing an instance of Key.
So, you might do that:
struct MyHash : std::hash<std::string_view> {
using is_transparent = std::true_type; // actual type doesn't matter
};
Now, for the equal_to
we can be lazy and use std::equal_to</*void*/>
which is already transparent:
#include <boost/unordered/unordered_flat_map.hpp>
using namespace std::literals;
struct MyHash : std::hash<std::string_view> {
using is_transparent = std::true_type; // actual type doesn't matter
};
int main() {
boost::unordered::unordered_flat_map<std::string, int, MyHash, std::equal_to<>> my_map;
auto [where,ok] = my_map.emplace("Hello", 1);
assert(ok);
auto found = my_map.find("Hello"sv);
assert(found != my_map.end());
assert(found == where);
}
Passing all asserts. In my code I would probably not rely on equal_to<void>
to prevent surprises. You could substitute Boost's "Rosetta Stone" core::string_view
¹. You could also make the hash even more tolerant (e.g. expressing it in terms of Boost ContainerHash).
¹ which is interoperable among many, like boost::beast::string_view and std::string_view and many others. It does, however, not support configurable char-traits.