Подтвердить что ты не робот

Std:: условный vs std:: enable_if

У меня есть функция хэширования, которая может принимать любой тип объекта и хеш файл, он использует std::hash внутренне. Поскольку std::hash не поддерживает типы перечислений, я создал перегрузки функции, 1 для перечислений с использованием std::underlying_type и 1 для других типов:

template <typename T, typename std::enable_if<std::is_enum<T>::value>::type* = nullptr>
static std::size_t Hash(T const & t)
{
    return std::hash<typename std::underlying_type<T>::type>()(t);
}

template <typename T, typename std::enable_if<!std::is_enum<T>::value>::type* = nullptr>
static std::size_t Hash(T const & t)
{
    return std::hash<T>()(t);
}

Это прекрасно работает. Затем я попытался перевести все это в одну функцию с помощью std::conditional:

template <typename T>
static std::size_t Hash(T const & t)
{
    typedef typename std::conditional<std::is_enum<T>::value, std::hash<typename std::underlying_type<T>::type>, std::hash<T>>::type Hasher;
    return Hasher()(t);
}

Main:

enum test
{
    TEST = 2
};

int main() {
    Hash<test>(TEST);
    Hash<int>(5);
    std::cin.get();
    return 0;
}

Это, однако, дало мне ошибка:

/usr/include/С++/5/type_traits: 2190: 38: ошибка: 'int' не является типом перечисления        Тип typedef __underlying_type (_Tp);

Я понимаю ошибку, но я не понимаю, почему, я думал, что std::conditional предотвратит эти ошибки времени компиляции, потому что <int> использует std::hash<T> вместо std::hash<typename std::underlying_type<T>::type> во время компиляции.

Что я делаю неправильно здесь, есть способ, которым я могу объединить 2 функции?

4b9b3361

Ответ 1

Проблема заключается в том, что std::underlying_type не является корректным, если аргумент шаблона не является перечислением, и обе ветки std::conditional должны быть действительными.

Одна из возможностей заключается в создании признака safe_underlying_type, который просто возвращает void, если T не является перечислением:

template <typename T, typename = typename std::is_enum<T>::type>
struct safe_underlying_type {
    using type = void;
};

template <typename T>
struct safe_underlying_type<T, std::true_type> {
    using type = std::underlying_type_t<T>; 
};

Или вы можете просто написать черту, чтобы получить желаемый тип хэша:

template <typename T, typename = typename std::is_enum<T>::type>
struct hash_type {
    using type = std::hash<T>;
};

template <typename T>
struct hash_type<T, std::true_type> {
    using type = std::hash<std::underlying_type_t<T>>;  
};

Ответ 2

Если вы действительно хотите использовать conditional, вам нужно будет отложить оценку:

template<class T> struct identity { using type = T; };

using Hasher = std::hash<typename std::conditional<std::is_enum<T>::value,
                                                   std::underlying_type<T>,
                                                   identity<T>>::type::type>;

Кроме того, std::hash должен изначально поддерживать перечисления с LWG 2148.

Ответ 3

Оба типа (ветки), используемые в conditional, должны быть хорошо сформированы, когда их оценивают во время компиляции.

Проблема заключается в том, что когда компилятор разбирает сам std::conditional, оператор typename std::underlying_type<T>::type должен быть хорошо сформирован (и это не для int). На самом деле не имеет значения, каков результат, он так далеко не достиг.

Ответ 4

typename std::underlying_type<T>::type

Это неверно сформировано для int, но оно все еще оценивается во время компиляции. Вот почему вы получаете эту ошибку. Вы уже дали решение с помощью std::enable_if, поэтому я не буду вдаваться в подробности. Я думаю, что все в порядке, и использование std::conditional просто усложняло бы чтение.