2016-02-02 15 views
13

Ich habe eine Hash-Funktion, die jeden Objekttyp und Hash-es kann, verwendet intern std::hash. Da std::hash nicht Enum-Typen unterstützen I Überlastungen der Funktion erstellt haben, 1 für Aufzählungen std::underlying_type und 1 für andere Arten mit:std :: konditional 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); 
} 

Dies funktioniert gut. Ich habe dann versucht es mit std::conditional alle in eine einzige Funktion zu setzen:

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

Dies ist jedoch mir eine error gab:

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

Ich verstehe die Fehler, aber ich verstehe nicht, warum, ich dachte, std::conditional würde diese Kompilierungszeit Fehler verhindern, weil <int> 01 verwendetanstelle von std::hash<typename std::underlying_type<T>::type> zur Kompilierzeit.

Was mache ich hier falsch, gibt es eine Möglichkeit, die 2 Funktionen zusammenzuführen?

+1

Kurz gesagt, "bedingt" erfordert, dass beide Zweige gut gebildet sind. Das Zusammenführen beider Zweige kann mit einer Merkmalsstruktur erfolgen. – Rostislav

Antwort

11

Das Problem ist, dass std::underlying_type nicht wohlgeformt ist, wenn das Template-Argument keine Aufzählung ist, und beide Zweige von std::conditional müssen gültig sein.

Eine Möglichkeit wäre ein safe_underlying_type Merkmal zu machen, die nur zurück void wenn T kein Enum ist:

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

Oder Sie könnten nur ein Merkmal schreiben den Hash-Typ erhalten Sie wollen:

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

Beide Typen (Zweige), die in conditional verwendet werden, müssen gut gebildet sein, wenn sie zur Kompilierzeit ausgewertet werden.

Das Problem ist, dass, wenn der Compiler die std::conditional selbst analysiert, muss die Anweisung typename std::underlying_type<T>::type gut gebildet werden (und es ist nicht für die int). Es ist nicht wirklich wichtig, was das Ergebnis ist, es ist noch nicht so weit gekommen.

1

typename std::underlying_type<T>::type

Dies ist für ein int nicht wohlgeformt, aber es ist nach wie vor bei der Kompilierung ausgewertet. Deshalb erhalten Sie diesen Fehler. Sie haben bereits eine Lösung mit std::enable_if angegeben, daher gehe ich hier nicht auf Details ein. Ich denke, es ist in Ordnung, und mit std::conditional würde es sowieso schwieriger zu lesen.

5

Wenn Sie wirklich conditional verwenden möchten, müssen Sie Bewertung aufzuschieben:

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

Außerdem sollte std::hash native Enums seit LWG 2148 unterstützen.

Verwandte Themen