Hello,
I'd like to search for elements in a key-based container via std::string_view without construction of std::string instance. It is possible for std::map/std::set if I explicitly declare geterogeneous (or transparent) compare function.
std::map<std::string, std::less<>> m;
m.find(std::string_view{"hello"});
It is done via generic lookup functions which are enabled if a compare function is transparent. Unfortunately, there are now such functions for unordered containers.
But it's rather old and I don't like it for several reasons. I believe the C++ committee dislikes it too)
1) It proposes heterogeneous hash function which forwards type T as std::decay<T>. I consider this a bad practice because std::decay can destroy initial type (remember array-to-pointer conversion).
2) Heterogeneous lookup is enabled by default
template<typename T, typename Hash = std::hash<>, typename Eq = std::equal_to<>>
iterator find(T t, Hash h = Hash(), Eq e = Eq());
That's wrong and breaks an old code
some_map.find({100, 'A'});
Also it makes the functions ugly.
Now I work on my own proposal to make heterogeneous lookup in unordered containers better.
Instead of implementing std::hash<> via std::decay<T> I propose more safe approach
template <>
struct hash<void> {
template <typename T>
size_t operator()(T&& v) const noexcept {
return hash<typename remove_cv<typename remove_reference<T>::type>::type>{}(v);
}
typedef void is_transparent;
};
If an unordered container is declared with transparent hash then the default compare function should be transparent too. It may look like this
template <class _Value, class _Hash>
struct _default_pred {
typedef equal_to<_Value> type;
};
template <class _Value>
struct _default_pred< _Value, hash<> > {
typedef equal_to<> type;
};
template <class _Value, class _Hash = hash<_Value>,
class _Pred = typename _default_pred<_Value, _Hash>::type,
class _Alloc = allocator<_Value> >
class unordered_set
Then the heterogeneous lookup functions may be enabled via SFINAE if the hash and compare functions are both transparent.
template <class _K2, class _Rt>
using __enable_if_transparent = typename enable_if<
__is_transparent<_Hash, _K2>::value &&
__is_transparent<_Pred, _K2>::value, _Rt>::type;
template <class _K2>
__enable_if_transparent<_K2, iterator> find(const _K2& __k);
The solution is the same for unordered set/multiset/map/multimap.
What do you think about all of this? Any thoughts would be appreciated.
Or maybe somebody has already done it better.
Thanks,