I have following code which detects if given type T
is std::hash
able. It checks validity of the passed parameters as well as if result of std::hash<T>::operator()
is convertible to size_t
:
template <typename T, typename = void>
struct IsStdHashable : std::false_type
{
};
template <typename T>
struct IsStdHashable<T, std::void_t<decltype(std::declval<std::hash<T>>()(std::declval<T>()))>> :
std::is_convertible<decltype(std::declval<std::hash<T>>()(std::declval<T>())), size_t>
{
};
template <typename T>
constexpr bool IsStdHashableV = IsStdHashable<T>::value;
template <typename ValueT>
concept StdHashable = IsStdHashableV<ValueT>;
It works very well but the syntax is ugly. It requires to double decltype
part. I'm wondering if there is any possibility to assign expression decltype(std::declval<std::hash<T>>()(std::declval<T>()))
to some RT
alias and use it in the above code.
Pseudocode:
template <typename T>
using RT = decltype(std::declval<std::hash<T>>()(std::declval<T>()))
struct IsStdHashable<T, std::void_t<RT>> : std::is_convertible<RT, size_t>
{
};
Is it even possible?
I was thinking a lot but it took me to nothing.
Sure, define it as a separate type alias template:
template <typename T>
using RT = decltype(std::declval<std::hash<T>>()(std::declval<T>()));
template <typename T>
struct IsStdHashable<T, std::void_t<RT<T>>> :
std::is_convertible<std::size_t, RT<T>> {};
But since you're using c++20, it'd be easier to test directly in the constraint, as concepts were intended:
template <typename T>
concept StdHashable = std::convertible_to<
std::size_t,
std::invoke_result_t<std::hash<T>, T>>;
or written another way:
template <typename T>
concept StdHashable = requires (std::hash<T> h, T t) {
{ h(t) } -> std::convertible_to<std::size_t>;
};