c++c++20sfinaec++-conceptsclass-template

Alias for class template parameter in C++


I have following code which detects if given type T is std::hashable. 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.


Solution

  • 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 , 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>;
    };