เหตุผลง่ายๆ คือไม่ได้เพิ่มเข้าไปในมาตรฐาน เช่นเดียวกับการแฮชโครงสร้างอื่นๆ เช่น tuple
สิ่งต่างๆ มักจะถูกเพิ่มเข้าไปในมาตรฐานเมื่อมันดีพอ ไม่ใช่เมื่อมันสมบูรณ์แบบ เพราะความสมบูรณ์แบบเป็นศัตรูของความดี ความเชี่ยวชาญพิเศษของ std::hash
ไม่ใช่สิ่งที่จะทำให้โค้ดเสียหาย (บ่อยครั้ง) ดังนั้นการเพิ่มสิ่งใหม่จึงไม่เป็นอันตราย
ไม่ว่าในกรณีใด เราสามารถเขียนส่วนขยายแฮชของเราเองได้ ตัวอย่างเช่น:
namespace hashers {
constexpr size_t hash_combine( size_t, size_t ); // steal from boost, or write your own
constexpr size_t hash_combine( size_t a ) { return a; }
constexpr size_t hash_combine() { return 0; }
template<class...Sizes>
constexpr size_t hash_combine( size_t a, size_t b, Sizes... sizes ) {
return hash_combine( hash_combine(a,b), sizes... );
}
template<class T=void> struct hash;
template<class A, class B>
constexpr size_t custom_hash( std::pair<A,B> const& p ) {
return hash_combine( hash<size_t>{}(2), hash<std::decay_t<A>>{}(p.first), hash<std::decay_t<B>>{}(p.second) );
}
template<class...Ts, size_t...Is>
constexpr size_t custom_hash( std::index_sequence<Is...>, std::tuple<Ts...> const& p ) {
return hash_combine( hash<size_t>{}(sizeof...(Ts)), hash<std::decay_t<Ts>>{}(std::get<Is>(p))... );
}
template<class...Ts>
constexpr size_t custom_hash( std::tuple<Ts...> const& p ) {
return custom_hash( std::index_sequence_for<Ts...>{}, p );
}
template<class T0, class C>
constexpr size_t custom_hash_container( size_t n, C const& c) {
size_t retval = hash<size_t>{}(n);
for( auto&& x : c)
retval = hash_combine( retval, hash<T>{}(x) );
return retval;
}
template<class T0, class C>
constexpr size_t custom_hash_container( C const& c) {
return custom_hash_container( c.size(), c );
}
template<class T, class...Ts>
size_t custom_hash( std::vector<T, Ts...> const& v ) {
return custom_hash_container<T>(v);
}
template<class T, class...Ts>
size_t custom_hash( std::basic_string<T, Ts...> const& v ) {
return custom_hash_container<T>(v);
}
template<class T, size_t n>
constexpr size_t custom_hash( std::array<T, n> const& v ) {
return custom_hash_container<T>(n, v);
}
template<class T, size_t n>
constexpr size_t custom_hash( T (const& v)[n] ) {
return custom_hash_container<T>(n, v);
}
// etc -- list, deque, map, unordered map, whatever you want to support
namespace details {
template<class T, class=void>
struct hash : std::hash<T> {};
using hashers::custom_hash;
template<class T>
struct hash<T,decltype(void(
custom_hash(declval<T const&>())
)) {
constexpr size_t operator()(T const& t)const {
return custom_hash(t);
}
};
}
template<class T>
struct hash : details::hash<T> {};
template<>
struct hash<void> {
template<class T>
constexpr size_t operator()(T const& t)const { return hash<T>{}(t); }
}
}
และตอนนี้ hashers::hash<T>
จะใช้ฟังก์ชัน custom_hash
ที่ค้นหาโดย ADL ซ้ำๆ หรือ std::hash
หากล้มเหลว เพื่อแฮช T
และส่วนประกอบต่างๆ และ hashers::hash<>
จะเป็นแฮชสากลที่พยายามแฮชอะไรก็ตามที่ส่งผ่านไป
โค้ดอาจไม่คอมไพล์ตามที่แสดง
ฉันเลือกที่จะแฮชคอนเทนเนอร์และทูเปิลทั้งหมดตามความยาวแฮช ตามด้วยการแฮชการรวมเนื้อหาเข้าด้วยกัน ผลข้างเคียง array<int, 3>
แฮชเหมือนกับ tuple<int,int,int>
และ tuple<int,int>
แฮชเหมือนกับ pair<int,int>
และ std::vector<char>{'a','b','c', '\0'}
แฮชเหมือนกับ "abc"
ซึ่งฉันคิดว่าเป็นคุณสมบัติที่ดี อาร์เรย์ว่าง/tuple/vector/etc แฮชเช่น size_t(0)
คุณสามารถขยายระบบข้างต้นสำหรับประเภทของคุณเองได้โดยเพียงแค่แทนที่ custom_hash
ในเนมสเปซของประเภทที่ต้องการ หรือเชี่ยวชาญ std::hash<X>
หรือ hashers::hash<X>
เพื่อทำแฮชที่คุณกำหนดเอง (ฉันจะใช้ std::hash
สำหรับหลักการที่ทำให้ตัวเองประหลาดใจน้อยที่สุด) สำหรับการใช้งานขั้นสูง คุณสามารถเชี่ยวชาญ hashers::details::hash<X,void>
ด้วย SFINAE ได้ แต่ฉันบอกให้ทำเพื่อ custom_hash
แทน
person
Yakk - Adam Nevraumont
schedule
14.01.2015
const myClass *
ได้ไหม? หรือคุณสามารถแฮชconst myClass
เท่านั้น? - person Dietrich Epp   schedule 14.01.2015std::hash
ของคุณเอง และใช้boost::hash_combine
เพื่อรวมแฮชแต่ละรายการได้ - person Praetorian   schedule 14.01.2015