मैं C ++ 0x में हैश वैल्यू को कैसे संयोजित करूं?


87

C ++ 0x जोड़ता है hash<...>(...)

मैं एक hash_combineसमारोह नहीं मिल सका , हालांकि के रूप में बढ़ावा दिया । ऐसा कुछ लागू करने का सबसे साफ तरीका क्या है? शायद, सी ++ 0x का उपयोग कर xor_combine?

जवाबों:


93

ठीक है, बस इसे ऐसे करें जैसे बूस्ट लोगों ने किया:

template <class T>
inline void hash_combine(std::size_t& seed, const T& v)
{
    std::hash<T> hasher;
    seed ^= hasher(v) + 0x9e3779b9 + (seed<<6) + (seed>>2);
}

26
हाँ, यह सबसे अच्छा मैं भी कर सकता है। मुझे समझ नहीं आ रहा है कि मानकों की समिति ने कैसे कुछ स्पष्ट किया।
नील जी

13
@ नील: मैं सहमत हूँ। मुझे लगता है कि उनके लिए एक सरल समाधान पुस्तकालय की आवश्यकता होगी std::pair(या tuple, यहां तक ​​कि) के लिए हैश । यह प्रत्येक तत्व के हैश की गणना करेगा, फिर उन्हें संयोजित करेगा। (और मानक पुस्तकालय की भावना में, एक क्रियान्वित तरीके से।)
GManNickG

3
मानक से हटकर बहुत सारी स्पष्ट चीजें हैं। गहन सहकर्मी समीक्षा की प्रक्रिया उन छोटी चीजों को दरवाजे से बाहर निकालना मुश्किल बनाती है।
stinky472

15
यहां ये मैजिक नंबर क्यों? और उपरोक्त मशीन पर निर्भर नहीं है (उदाहरण के लिए यह x86 और x64 प्लेटफार्मों पर अलग नहीं होगा)?
einpoklum

3
यहाँ एक पेपर हैश_कोम्बिन को शामिल करने का सुझाव दिया गया है
SSJ_GZ

35

मैं इसे यहाँ साझा करूँगा क्योंकि यह इस समाधान की तलाश करने वाले अन्य लोगों के लिए उपयोगी हो सकता है: @KarlvonMoor उत्तर से शुरू होकर , यहाँ एक वैरेडिक टेम्प्लेट संस्करण है, जो यदि आपके उपयोग में एक साथ कई मानों को संयोजित करता है:

inline void hash_combine(std::size_t& seed) { }

template <typename T, typename... Rest>
inline void hash_combine(std::size_t& seed, const T& v, Rest... rest) {
    std::hash<T> hasher;
    seed ^= hasher(v) + 0x9e3779b9 + (seed<<6) + (seed>>2);
    hash_combine(seed, rest...);
}

उपयोग:

std::size_t h=0;
hash_combine(h, obj1, obj2, obj3);

यह मूल रूप से कस्टम प्रकार को आसानी से धोने योग्य बनाने के लिए एक वैरेडिक मैक्रो को लागू करने के लिए लिखा गया था (जो मुझे लगता है कि एक hash_combineसमारोह के प्राथमिक उपयोगों में से एक है):

#define MAKE_HASHABLE(type, ...) \
    namespace std {\
        template<> struct hash<type> {\
            std::size_t operator()(const type &t) const {\
                std::size_t ret = 0;\
                hash_combine(ret, __VA_ARGS__);\
                return ret;\
            }\
        };\
    }

उपयोग:

struct SomeHashKey {
    std::string key1;
    std::string key2;
    bool key3;
};

MAKE_HASHABLE(SomeHashKey, t.key1, t.key2, t.key3)
// now you can use SomeHashKey as key of an std::unordered_map

बीज को क्रमशः 6 और 2 से क्यों विभाजित किया जाता है?
j00hi

5

इसे निम्नानुसार एक वैचारिक टेम्पलेट का उपयोग करके भी हल किया जा सकता है:

#include <functional>

template <typename...> struct hash;

template<typename T> 
struct hash<T> 
    : public std::hash<T>
{
    using std::hash<T>::hash;
};


template <typename T, typename... Rest>
struct hash<T, Rest...>
{
    inline std::size_t operator()(const T& v, const Rest&... rest) {
        std::size_t seed = hash<Rest...>{}(rest...);
        seed ^= hash<T>{}(v) + 0x9e3779b9 + (seed << 6) + (seed >> 2);
        return seed;
    }
};

उपयोग:

#include <string>

int main(int,char**)
{
    hash<int, float, double, std::string> hasher;
    std::size_t h = hasher(1, 0.2f, 2.0, "Hello World!");
}

कोई निश्चित रूप से एक टेम्पलेट फ़ंक्शन कर सकता है, लेकिन यह कुछ बुरा प्रकार की कटौती का कारण बन सकता है उदाहरण के hash("Hallo World!")लिए स्ट्रिंग पर बजाय सूचक पर एक हैश मान की गणना करेगा। शायद यही कारण है, क्यों मानक एक संरचना का उपयोग करता है।


4

कुछ दिनों पहले मैं इस उत्तर के थोड़े बेहतर संस्करण के साथ आया था (C ++ 17 समर्थन की आवश्यकता है):

template <typename T, typename... Rest>
void hashCombine(uint& seed, const T& v, Rest... rest)
{
    seed ^= ::qHash(v) + 0x9e3779b9 + (seed << 6) + (seed >> 2);
    (hashCombine(seed, rest), ...);
}

कोड जनरेशन के लिहाज से ऊपर का कोड बेहतर है। मैंने अपने कोड में Qt से qHash फ़ंक्शन का उपयोग किया, लेकिन किसी अन्य हैशर्स का उपयोग करना भी संभव है।


के रूप में गुना अभिव्यक्ति लिखें (int[]){0, (hashCombine(seed, rest), 0)...};और यह C ++ 11 में भी काम करेगा।
हेनरी मेनके

3

मैं वास्तव में vt4a2h द्वारा उत्तर से C ++ 17 दृष्टिकोण को पसंद करता हूं , हालांकि यह एक समस्या से ग्रस्त है: Restमूल्य द्वारा पारित किया गया है जबकि उन्हें कॉन्स्टिट्यूशन संदर्भों द्वारा पारित करना अधिक वांछनीय होगा (जो कि एक होना चाहिए चाल-प्रकार के साथ प्रयोग करने योग्य)।

यहाँ अनुकूलित संस्करण है जो अभी भी एक गुना अभिव्यक्ति का उपयोग करता है (यही कारण है कि इसके लिए C ++ 17 या इसके बाद के संस्करण की आवश्यकता होती है) और std::hash(Qt हैश फ़ंक्शन के बजाय ) का उपयोग करता है :

template <typename T, typename... Rest>
void hash_combine(std::size_t& seed, const T& v, const Rest&... rest)
{
    seed ^= std::hash<T>{}(v) + 0x9e3779b9 + (seed << 6) + (seed >> 2);
    (hash_combine(seed, rest), ...);
}

पूर्णता के लिए: सभी प्रकार जो इस संस्करण के साथ उपयोग करने योग्य होंगे , उनके नाम में इंजेक्शन के लिए टेम्पलेट विशेषज्ञताhash_combine होनी चाहिए ।hashstd

उदाहरण:

namespace std // Inject hash for B into std::
{
    template<> struct hash<B>
    {
        std::size_t operator()(B const& b) const noexcept
        {
            std::size_t h = 0;
            cgb::hash_combine(h, b.firstMember, b.secondMember, b.andSoOn);
            return h;
        }
    };
}

ताकि Bऊपर के उदाहरण में भी एक और प्रकार के भीतर प्रयोग करने योग्य हो A, जैसे निम्न उपयोग उदाहरण दिखाता है:

struct A
{
    std::string mString;
    int mInt;
    B mB;
    B* mPointer;
}

namespace std // Inject hash for A into std::
{
    template<> struct hash<A>
    {
        std::size_t operator()(A const& a) const noexcept
        {
            std::size_t h = 0;
            cgb::hash_combine(h,
                a.mString,
                a.mInt,
                a.mB, // calls the template specialization from above for B
                a.mPointer // does not call the template specialization but one for pointers from the standard template library
            );
            return h;
        }
    };
}

मेरी राय में यह Hashमानक कंटेनरों के टेम्प्लेट तर्कों का उपयोग करने के लिए है, जो आपके कस्टम हैशर को नामांकित स्थान पर इंजेक्ट करने के बजाय निर्दिष्ट करता stdहै।
हेनरी मेन्के

3

Vt4a2h द्वारा जवाब निश्चित रूप से अच्छा है, लेकिन सी ++ 17 गुना अभिव्यक्ति का उपयोग करता है और हर कोई आसानी से एक नए toolchain करने के लिए स्विच करने में सक्षम है। नीचे दिया गया संस्करण गुना अभिव्यक्ति का अनुकरण करने के लिए विस्तारक चाल का उपयोग करता है और C ++ 11 और C ++ 14 में भी काम करता है ।

इसके अतिरिक्त, मैंने फ़ंक्शन को चिह्नित किया inlineऔर varadic टेम्पलेट तर्क के लिए एकदम सही अग्रेषण का उपयोग किया।

template <typename T, typename... Rest>
inline void hashCombine(std::size_t &seed, T const &v, Rest &&... rest) {
    std::hash<T> hasher;
    seed ^= hasher(v) + 0x9e3779b9 + (seed << 6) + (seed >> 2);
    (int[]){0, (hashCombine(seed, std::forward<Rest>(rest)), 0)...};
}

संकलक एक्सप्लोरर पर लाइव उदाहरण


बहुत अच्छा लग रहा है, धन्यवाद! मैं शायद मूल्य से गुजरने के बारे में परवाह नहीं करता था, क्योंकि मैंने कुछ अंतर्निहित साझा वस्तुओं का उपयोग किया था, उदाहरण के लिए, जैसे QString।
vt4a2h
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.