2012-03-10 50 views
9

我正在爲我的對象寫一個散列函數。我已經可以散列容器,並結合散列,感謝Generic Hash function for all STL-containers。但我的課也有枚舉。當然,我可以爲每個枚舉創建一個哈希函數,但似乎並不是一個好主意。是否有可能爲std::hash創建一些通用規範,以便它可以應用於每個枚舉?類似的東西,使用std::enable_ifstd::is_enumC++ 11任何枚舉類型的散列函數

namespace std { 
    template <class E> 
    class hash<typename std::enable_if<std::is_enum<E>::value, E>::type> { 
    public: 
    size_t operator()(const E& e) const { 
     return std::hash<std::underlying_type<E>::type>()(e); 
    } 
    }; 
}; 

PS。此代碼不能編譯

error: template parameters not used in partial specialization: 
error:   ‘E’ 

回答

4

這是可能的,通過將專業化失敗移出模板參數列表,以便仍然可以發生E的參數推導。例如,你可以有它發生在一個using聲明:(我通常使用的類型名稱sfinae讓我記得我爲什麼它的存在)

namespace std { 
    template<class E>class hash { 
    using sfinae = typename std::enable_if<std::is_enum<E>::value, E>::type; 
    public: 
    size_t operator()(const E&e) const { 
     return std::hash<typename std::underlying_type<E>::type>()(e); 
    } 
    }; 
}; 

演示代碼:

#include <functional> 

namespace std { 
    template<class E>class hash { 
    using sfinae = typename std::enable_if<std::is_enum<E>::value, E>::type; 
    public: 
    size_t operator()(const E&e) const { 
     return std::hash<typename std::underlying_type<E>::type>()(e); 
    } 
    }; 
}; 

enum foo { BAR, BAZ }; 
class quux {}; 

int main() { 
    // Compiles. 
    std::hash<foo>foo_hash; 
    // Gives a compile-time error: no type named ‘type’ in ‘struct 
    // std::enable_if<false, quux>’. 
    std::hash<quux>quux_hash; 
    return 0; 
} 
+0

這個'template class hash'定義了一個主模板'hash',而不是專門化。專業化看起來像:'template class hash >'。我真的不認爲你可以重新定義主要模板'std :: hash '。 – cdyson37 2015-01-29 17:26:12

+0

@ cdyson37此外,你所提到的似乎是_partial specialization_完整的專業化看起來像'template <>結構散列 {...}; ** **,部分專業化是違背標準,並指定爲**未定義的行爲**。看到這[SO問題/答案](http://stackoverflow.com/questions/28077592/extending-namespace-std-via-partial-template-specialization)。 – 2015-02-02 03:48:03

+1

這確實是一個部分專業化。但我相信這是允許的 - 看看[這裏](http://stackoverflow.com/questions/23339298/stdhash-template-partial-specialization) – cdyson37 2015-02-11 15:03:01

10

E參數不能被推斷,因爲編譯器無法知道你的enable_if<...>::type最終再次表示E(事實上,也有它的一些特例,通過設計不做那!)。它被稱爲E的「非推斷上下文」。

如果hash只有一個參數,那麼SFINAE沒有辦法(我知道)將你的部分專業化。

4

如果你願意使用宏,你可以在你的枚舉聲明旁邊轉儲正確的std :: hash專用。

否則,我發現很容易哈希枚舉值的唯一途徑是推廣散列值類型:

struct enum_hash 
{ 
    template <typename T> 
    inline 
    typename std::enable_if<std::is_enum<T>::value, std::size_t>::type 
    operator()(T const value) const 
    { 
     return static_cast<std::size_t>(value); 
    } 
}; 

,並使用它的方式:

enum class E { a, b, c }; 
std::unordered_map<E, std:string, enum_hash> map; 
map[E::a] = "a"; 
2

你想要做的是標準禁止的。

[namespace.std]

一個C的行爲++程序是,如果其添加聲明或 德音響nitions除非另有規定空間std 內空間std或一個命名空間理解過程音響定義。

程序可以添加一個模板專業化任何 標準庫模板,空間std只有在聲明 取決於用戶自網絡斯內德類型和專業化符合原始模板的 標準庫的要求,而不是 明確禁止。

所以你當然可以追求這些答案中的一些想法,但你不能稱之爲std :: hash。定義你自己的'enum_hash'模板似乎是一個好主意。