2016-02-02 5 views
13

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

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

Это, однако, дал мне error:

/usr/include/c++/5/type_traits:2190:38: error: 'int' is not an enumeration type typedef __underlying_type(_Tp) type;

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

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

+1

Короче говоря, 'условный' требует, чтобы обе ветви были хорошо сформированы. Слияние обеих ветвей может быть выполнено с помощью структуры признаков. – Rostislav

ответ

11

Проблема заключается в том, что 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>>; 
}; 
1

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

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

1

typename std::underlying_type<T>::type

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

5

Если вы действительно хотите использовать 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.

Смежные вопросы