कैसे unded कंटेनरों में उपयोगकर्ता परिभाषित प्रकार के लिए std :: हैश <कुंजी> :: ऑपरेटर () के विशेषज्ञ?


101

में उपयोगकर्ता परिभाषित कुंजी प्रकार का समर्थन करने के std::unordered_set<Key>लिए और std::unordered_map<Key, Value> एक प्रदान करने के लिए है operator==(Key, Key)और एक हैश functor:

struct X { int id; /* ... */ };
bool operator==(X a, X b) { return a.id == b.id; }

struct MyHash {
  size_t operator()(const X& x) const { return std::hash<int>()(x.id); }
};

std::unordered_set<X, MyHash> s;

टाइप करने के लिए डिफ़ॉल्ट हैश केstd::unordered_set<X> साथ लिखना अधिक सुविधाजनक होगा , जैसे कंपाइलर और लाइब्रेरी के साथ आने वाले प्रकार। परामर्श के बादX

यह संभव लगता है विशेषज्ञ std::hash<X>::operator():

namespace std { // argh!
  template <>
  inline size_t 
  hash<X>::operator()(const X& x) const { return hash<int>()(x.id); } // works for MS VC10, but not for g++
  // or
  // hash<X>::operator()(X x) const { return hash<int>()(x.id); }     // works for g++ 4.7, but not for VC10 
}                                                                             

सी ++ 11 के लिए संकलक समर्थन को देखते हुए अभी तक प्रयोगात्मक है --- मैंने क्लैंग --- की कोशिश नहीं की, ये मेरे सवाल हैं:

  1. क्या नामस्थान में ऐसी विशेषज्ञता जोड़ना कानूनी है std? उसके बारे में मेरी मिश्रित भावनाएँ हैं।

  2. कौन सा std::hash<X>::operator()संस्करण, यदि कोई हो, C ++ 11 मानक के अनुरूप है?

  3. क्या ऐसा करने का कोई पोर्टेबल तरीका है?


4.7.2 के साथ, मुझे operator==(const Key, const Key)
विक्टर ह्युबोस्लावस्की

ध्यान दें कि std::hash( stdनाम स्थान की अन्य बातों के विपरीत ) की विशेषज्ञता Google शैली मार्गदर्शिका द्वारा हतोत्साहित की जाती है ; नमक के साथ ले।
फ्रैंकलिन यू

जवाबों:


128

आपको स्पष्ट रूप से अनुमति दी जाती है और नामस्थान * पर विशेषज्ञता जोड़ने के लिए प्रोत्साहित किया जाता है std। हैश फ़ंक्शन जोड़ने का सही (और मूल रूप से केवल) तरीका यह है:

namespace std {
  template <> struct hash<Foo>
  {
    size_t operator()(const Foo & x) const
    {
      /* your code here, e.g. "return hash<int>()(x.value);" */
    }
  };
}

(अन्य लोकप्रिय विशेषज्ञ जिन्हें आप समर्थन करने पर विचार कर सकते हैं std::less, std::equal_toऔर std::swap)

*) जब तक शामिल प्रकारों में से एक उपयोगकर्ता-परिभाषित है, मुझे लगता है।


3
जबकि यह संभव है, क्या आप इसे इस तरह से करने की सलाह देंगे? मैं unorder_map<eltype, hash, equality>इसके बजाय तात्कालिकता पसंद करूंगा , किसी के दिन को मजाकिया एडीएल कारोबार से बर्बाद करने से बचना। ( इस विषय पर पीट बेकर की सलाह को संपादित करें )
सेह

2
@sehe: यदि आपके पास एक हैश फ़नकार पड़ा हुआ है जो डिफ़ॉल्ट रूप से रचनात्मक है, शायद, लेकिन क्यों? (समानता आसान है, क्योंकि आप केवल सदस्य को लागू करेंगे- operator==।) मेरा सामान्य दर्शन यह है कि यदि फ़ंक्शन प्राकृतिक है और अनिवार्य रूप से केवल "सही" एक (जैसे लेक्सिकोग्राफिक जोड़ी तुलना) है, तो मैं इसे जोड़ता हूं std। यदि यह कुछ अजीबोगरीब है (जैसे अनियंत्रित जोड़ी तुलना), तो मैं इसे कंटेनर प्रकार के लिए विशिष्ट बनाता हूं।
केरेक एसबी

3
मैं असहमत नहीं हूं, लेकिन मानक में हमें अनुमति दी जाती है और एसटीडी में विशेषज्ञता जोड़ने के लिए कैसे प्रोत्साहित किया जाता है?
रजि।

3
@ केरैक, मैं सहमत हूं, लेकिन मैं मानक में एक स्थान के लिए एक अध्याय और कविता संदर्भ की उम्मीद कर रहा था। मुझे 17.6.4.2.1 पर इंजेक्शन की अनुमति देने वाला शब्द मिला जहां यह कहता है कि "जब तक कि निर्दिष्ट नहीं किया गया है" तब तक इसकी अनुमति नहीं है, लेकिन मैं 4000+ पृष्ठ विनिर्देश के बीच "अन्यथा निर्दिष्ट" भाग को खोजने में सक्षम नहीं हूं।
14

3
@ यहां आप पढ़ सकते हैं "एक प्रोग्राम किसी भी मानक लाइब्रेरी टेम्पलेट के लिए टेम्प्लेट स्पेशलाइजेशन जोड़ सकता है, नामस्थान std के लिए केवल तभी घोषित किया जा सकता है जब डिक्लेरेशन यूज़र-डिफ़ाइंड टाइप पर निर्भर करता है और स्पेशलाइज़ेशन मूल टेम्पलेट के लिए स्टैण्डर्ड लाइब्रेरी आवश्यकताओं को पूरा करता है और स्पष्ट रूप से निषिद्ध नहीं है। । "। तो यह समाधान ठीक है।
मारेक आर।

7

मेरी शर्त हाश टेम्पलेट तर्क पर unordered_map / unorder_set / ... कक्षाओं के लिए होगी:

#include <unordered_set>
#include <functional>

struct X 
{
    int x, y;
    std::size_t gethash() const { return (x*39)^y; }
};

typedef std::unordered_set<X, std::size_t(*)(const X&)> Xunset;
typedef std::unordered_set<X, std::function<std::size_t(const X&)> > Xunset2;

int main()
{
    auto hashX = [](const X&x) { return x.gethash(); };

    Xunset  my_set (0, hashX);
    Xunset2 my_set2(0, hashX); // if you prefer a more flexible set typedef
}

बेशक

  • हैशक्स सिर्फ एक वैश्विक स्थिर कार्य हो सकता है
  • दूसरे मामले में, आप इसे पारित कर सकते हैं
    • पुराने फफूंददार फनकार वस्तु ( struct Xhasher { size_t operator(const X&) const; };)
    • std::hash<X>()
    • हस्ताक्षर को संतुष्ट करने वाली कोई भी बाँध अभिव्यक्ति -

मैं सराहना करता हूं कि आप कुछ ऐसा लिख ​​सकते हैं जिसमें डिफ़ॉल्ट कंस्ट्रक्टर नहीं है, लेकिन मुझे हमेशा लगता है कि अतिरिक्त तर्क को याद रखने के लिए प्रत्येक मानचित्र निर्माण की आवश्यकता होती है जो थोड़ा बोझ है - मेरे स्वाद के लिए बहुत अधिक बोझ। मैं एक स्पष्ट टेम्पलेट तर्क के साथ ठीक हूं, हालांकि विशेषज्ञता std::hashअभी भी सबसे अच्छा तरीका है :-)
केरेक एसबी

बचाव के लिए उपयोगकर्ता-परिभाषित प्रकार :-) अधिक गंभीरता से, मुझे आशा है कि हम उन्हें कलाई पर थप्पड़ पहले ही मंच पर दे देंगे जहां उनकी कक्षा में एक है char*!
केरेक एसबी नोव 16'11

हम्म ... क्या आपके पास एक उदाहरण है कि कैसे एक hashविशेषज्ञता ADL के माध्यम से हस्तक्षेप करती है? मेरा मतलब है, यह पूरी तरह से प्रशंसनीय है, लेकिन मेरे पास एक कठिन समय है जो एक समस्या का मामला है।
केरेक एसबी


यह सब मजेदार और खेल है जब तक आपको ज़रूरत नहीं है std::unordered_map<Whatever, Xunset>और यह काम नहीं करता है क्योंकि आपका Xunsetहैशर प्रकार डिफ़ॉल्ट रूप से रचनात्मक नहीं है।
ब्रायन गॉर्डन

4

@Kerrek SB ने 1) और 3 को कवर किया है।

2) भले ही जी ++ और वीसी 10 std::hash<T>::operator()विभिन्न हस्ताक्षर के साथ घोषित होते हैं, दोनों पुस्तकालय कार्यान्वयन मानक अनुपालन हैं।

मानक के सदस्यों को निर्दिष्ट नहीं करता है std::hash<T>। यह सिर्फ यह कहता है कि इस तरह के प्रत्येक विशेषज्ञता को उसी तरह के "हैश" आवश्यकताओं को पूरा करना चाहिए std::unordered_setताकि दूसरे टेम्पलेट तर्क के लिए आवश्यक हो। अर्थात्:

  • हैश प्रकार Hएक फ़ंक्शन ऑब्जेक्ट है, जिसमें कम से कम एक तर्क प्रकार होता है Key
  • H प्रतिलिपि निर्माण योग्य है।
  • H विनाशकारी है।
  • यदि hएक प्रकार की अभिव्यक्ति है Hया const H, k(संभवतः const) के लिए परिवर्तनीय प्रकार की अभिव्यक्ति है Key, तो h(k)प्रकार के साथ एक वैध अभिव्यक्ति है size_t
  • यदि hप्रकार की अभिव्यक्ति है Hया const H, और प्रकार uका एक अंतराल है Key, तो h(u)प्रकार के साथ एक वैध अभिव्यक्ति है size_tजो संशोधित नहीं करता है u

नहीं, न तो कार्यान्वयन मानक अनुपालन है, क्योंकि वे एक पूरे के std::hash<X>::operator()बजाय विशेषज्ञ करने का प्रयास करते हैं std::hash<X>, और std::hash<T>::operator()कार्यान्वयन के हस्ताक्षर को परिभाषित किया जाता है।
०१

@ मिल्डजर्न: स्पष्ट - मैं पुस्तकालय कार्यान्वयन के बारे में बात कर रहा था, न कि विशेषीकृत विशेषज्ञता। निश्चित नहीं है कि वास्तव में ओपी से क्या पूछना है।
aschepler
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.