2012-10-05 15 views
16

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

template<typename T> std::true_type hashable_helper(
    const T&, const typename std::hash<T>::argument_type* = nullptr); 

template<typename T> std::false_type hashable_helper(...); 

//It won't let me derive from decltype directly, why? 
template<typename T> struct is_hashable 
    : std::is_same<decltype(hashable_helper<T>(std::declval<T>())), 
        std::true_type> {}; 

लेकिन तब मैंने सीखा, कि जीसीसी 4.7 और वीसी ++ 2012 किसी भी प्रकार T के लिए परिभाषित करें, गैर-विशिष्ट संस्करण में केवल static_assert आईएनजी। लेकिन सशर्त रूप से संकलित करने के बजाय वे (और क्लैंग 3.1जीसीसी 4.7 के libstdC++) का दावा विफलता के परिणामस्वरूप एक संकलन त्रुटि में विफल रहता है। ऐसा लगता है क्योंकि मुझे लगता है कि static_assert एस को SFINAE (दाएं?) द्वारा नियंत्रित नहीं किया जाता है, इसलिए एक SFINAE समाधान संभवतः बिल्कुल नहीं लगता है। gcc 4.6 के लिए यह और भी बदतर है, जिसमें सामान्य std::hash टेम्पलेट में भी नहीं है लेकिन को () ऑपरेटर को परिभाषित नहीं करता है, जिसके परिणामस्वरूप इसका उपयोग करने का प्रयास करते समय एक लिंकर त्रुटि होती है (जो संकलन त्रुटि से हमेशा खराब होती है और मैं एक लिंकर त्रुटि को एक कंपाइलर त्रुटि में बदलने के लिए किसी भी तरह की कल्पना नहीं कर सकता)।

तो वहाँ किसी भी मानक-conformant और पोर्टेबल तरह से लौटने अगर एक प्रकार एक वैध std::hash विशेषज्ञता, या हो सकता है है इस तरह के एक प्रकार विशेषता परिभाषित करने के लिए कम से कम पुस्तकालयों static_assert सामान्य टेम्पलेट में ing (किसी भी तरह में static_assert त्रुटि बदलने के लिए एक SFINAE गैर त्रुटि)?

+0

संपादित करें: ठीक है, के बारे में मेरे बयान * कुलपति ++ * संकलन यह कुछ अलग पुराने संस्करण से वास्तव में, * कुलपति ++ * बर्ताव करता है जैसे * जीसीसी *, पर घुट 'static_assert' –

+0

लगता है कि जीसीसी लोग जानते हैं इस बीच समस्या का। ऐसा कहा जाता है कि जीसीसी 4.8 में अब यह स्थिर जोर नहीं है, लेकिन वे भविष्य में मानक कार्यान्वयन करने पर विचार करते हैं, कुछ हद तक बूस्ट हैश के समान है, जहां कार्यान्वयन एडीएल द्वारा उठाया जाता है। – Ichthyo

+0

https://gcc.gnu.org/ml/libstdc++/2013-03/msg00029.html – Ichthyo

उत्तर

6

ऐसा लगता है हम दो परस्पर विरोधी आवश्यकताओं है:

  1. SFINAE एक टेम्पलेट के किसी भी इन्स्टेन्शियशन से बचने के लिए अगर इन्स्टेन्शियशन असफल और अधिभार सेट से इसी समारोह समाप्त हो सकती हैं के लिए है।
  2. static_assert() एक त्रुटि बनाने के लिए है, उदाहरण के लिए, टेम्पलेट के तत्काल के दौरान।

मेरे दिमाग में, 1. स्पष्ट रूप से ट्रम्प 2., यानी, आपके SFINAE को काम करना चाहिए। दो अलग संकलक विक्रेताओं के दिखने से असहमत, दुर्भाग्य से खुद के बीच नहीं बल्कि मेरे साथ। मानक यह निर्दिष्ट नहीं करता है कि std::hash<T> की डिफ़ॉल्ट परिभाषा कैसा दिखती है और केवल उन मामलों के लिए बाधा लगाती है जहां std::hash<T> एक प्रकार T के लिए विशिष्ट है।

मुझे लगता है कि आपके प्रस्तावित प्रकार के लक्षण एक उचित विचार हैं और इसे समर्थित किया जाना चाहिए। हालांकि, ऐसा लगता है कि मानक गारंटी नहीं देता है कि इसे कार्यान्वित किया जा सकता है। यह संकलक विक्रेताओं के साथ लाने और/या मानक के लिए एक दोष रिपोर्ट दर्ज करने लायक हो सकता है: वर्तमान विनिर्देश स्पष्ट मार्गदर्शन नहीं देता है कि क्या होना चाहिए, जहां तक ​​मैं कह सकता हूं। ... और यदि विनिर्देश वर्तमान में अनिवार्य है कि उपरोक्त के रूप में एक प्रकार का गुण विफल रहता है तो यह एक डिज़ाइन त्रुटि हो सकती है जिसे सही करने की आवश्यकता है।

+3

_ दो अलग संकलक विक्रेताओं के दिखने से असहमत, दुर्भाग्य से खुद के बीच नहीं, लेकिन me_ - lol – sehe

+0

आपके दिमाग में 1. ट्रम्प हो सकता है 2., लेकिन मानक असहमत, असहमत। इस प्रकार इसे अस्वीकार करने के लिए कंपाइलर्स पूरी तरह से सही हैं। – fgp

+0

@fgp क्या आप इस असहमति को साबित करने वाले मानक से कुछ उद्धरण प्रदान कर सकते हैं? –

3

यहां आपकी समस्या का एक बहुत गंदा समाधान है: यह जीसीसी 4.7 (और 4 नहीं) के लिए काम करता है।6, कारण लापता सुविधा के लिए सी ++ 11: mangling अधिभार)

// is_hashable.h 
namespace std { 
    template <class T> 
    struct hash { 
     typedef int not_hashable; 
    }; 

} 

#define hash hash_ 
#define _Hash_impl _Hash_impl_ 
#include<functional> 
#undef hash 
#undef _Hash_impl 

namespace std { 
    struct _Hash_impl: public std::_Hash_impl_{ 
     template <typename... Args> 
      static auto hash(Args&&... args) 
       -> decltype(hash_(std::forward<Args>(args)...)) { 
      return hash_(std::forward<Args>(args)...); 
     } 
    }; 
    template<> struct hash<bool>: public hash_<bool> {}; 
    // do this exhaustively for all the hashed standard types listed in: 
    // http://en.cppreference.com/w/cpp/utility/hash 
} 

template <typename T> 
class is_hashable 
{ 
    typedef char one; 
    typedef long two; 

    template <typename C> static one test(typename std::hash<C>::not_hashable) ; 
    template <typename C> static two test(...); 


public: 
    enum { value = sizeof(test<T>(0)) == sizeof(long) }; 
}; 


// main.cpp 
// #include "is_hashable.h" 
#include<iostream> 
#include<unordered_set> 

class C {}; 

class D { 
public: 
    bool operator== (const D & other) const {return true;} 
}; 

namespace std { 
    template <> struct hash<D> { 
     size_t operator()(const D & d) const { return 0;} 
    }; 
} 

int main() { 
    std::unordered_set<bool> boolset; 
    boolset.insert(true); 
    std::unordered_set<D> dset; 
    dset.insert(D());// so the hash table functions 
    std::cout<<is_hashable<bool>::value<<", "; 
    std::cout<<is_hashable<C>::value << ", "; 
    std::cout<<is_hashable<D>::value << "\n"; 
} 

और उत्पादन होता है:

1, 0, 1

हम मूल रूप से "अपहरण" हैश प्रतीक और इसमें कुछ सहायक typedef इंजेक्ट करें। आपको इसे VC++ के लिए संशोधित करने की आवश्यकता होगी, विशेष रूप से, _Hash_impl::hash() के लिए फ़िक्स, क्योंकि यह कार्यान्वयन विवरण है।

आप यह सुनिश्चित करें कि खंड is_hashable.h के रूप में लेबल के रूप में पहली शामिल इस गंदे चाल काम करना चाहिए शामिल है करते हैं ...

+0

इंजेक्शन और एक ही समय में डरावना! मुझे यह पसंद है, जब तक कि यह * कोड * में कभी भी दिखाई नहीं दे रहा है, जिसे मुझे काम करना है ;-) – fgp

+0

@fgp encapsulation के लिए बहुत कुछ :) – enobayram

0

मैं यह भी मारा। मैंने कुछ कामकाज की कोशिश की और std::hash<> के लिए एक श्वेतसूची फ़िल्टर के साथ चला गया। श्वेतसूची बनाए रखने के लिए सुखद नहीं है, लेकिन यह सुरक्षित है और यह काम करता है।

मैंने इसे वीएस 2013, 2015, क्लैंग और जीसीसी पर करने की कोशिश की।

#include <iostream> 
#include <type_traits> 

// based on Walter Brown's void_t proposal 
// http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2014/n3911.pdf 
namespace detail { 
    template<class... TN> struct void_t {typedef void type;}; 
} 
template<class... TN> 
struct void_t {typedef typename detail::void_t<TN...>::type type;}; 

// extensible whitelist for std::hash<> 
template <class T, typename = void> 
struct filtered_hash; 
template <class T> 
struct filtered_hash<T, 
    typename std::enable_if<std::is_enum<T>::value>::type> 
    : std::hash<T> { 
}; 
template <class T> 
struct filtered_hash<T, 
    typename std::enable_if<std::is_integral<T>::value>::type> 
    : std::hash<T> { 
}; 
template <class T> 
struct filtered_hash<T, 
    typename std::enable_if<std::is_pointer<T>::value>::type> 
    : std::hash<T> { 
}; 

template<typename, typename = void> 
struct is_hashable 
    : std::false_type {}; 

template<typename T> 
struct is_hashable<T, 
    typename void_t< 
     typename filtered_hash<T>::result_type, 
     typename filtered_hash<T>::argument_type, 
     typename std::result_of<filtered_hash<T>(T)>::type>::type> 
    : std::true_type {}; 

// try it out.. 
struct NotHashable {}; 

static_assert(is_hashable<int>::value, "int not hashable?!"); 
static_assert(!is_hashable<NotHashable>::value, "NotHashable hashable?!"); 

int main() 
{ 
    std::cout << "Hello, world!\n"; 
} 
+0

लेकिन यदि आपको आवश्यकता हो तो पहले फ़िल्टर में 'filtered_hash' के साथ जटिल कामकाज क्यों वैसे भी सभी विशेषज्ञताओं को सूचीबद्ध करने के लिए। क्यों न केवल सभी प्रकार के लिए 'is_hashable' विशेषज्ञ है? –

+0

यह उपयोग में लचीलापन प्रदान करता है। मैं std :: हैश के स्थान पर filtered_hash का उपयोग कर सकता हूं या std :: हैश उपयोग की सुरक्षा के लिए is_hashable का उपयोग कर सकता हूं। –

 संबंधित मुद्दे

  • कोई संबंधित समस्या नहीं^_^