정렬되지 않은 컨테이너의 사용자 정의 유형에 대해 std :: hash <Key> :: operator ()를 전문화하는 방법은 무엇입니까?


101

에 사용자 정의 키 유형을 지원하기 위해 std::unordered_set<Key>그리고 std::unordered_map<Key, Value> 하나는 제공해야 operator==(Key, Key)하고 해시 펑터 :

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유형 컴파일러 및 라이브러리와 함께 주셔서처럼. 상담 후

  • C ++ 표준 초안 N3242 §20.8.12 [unord.hash] 및 §17.6.3.4 [hash.requirements],
  • 부스트.
  • g ++ include\c++\4.7.0\bits\functional_hash.h
  • VC10 include\xfunctional
  • Stack Overflow의 다양한 관련 질문

전문화가 가능한 것 같습니다 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 
}                                                                             

C ++ 11에 대한 컴파일러 지원이 아직 실험적이라는 점을 감안할 때 --- 나는 Clang을 시도하지 않았습니다 ----, 내 질문은 다음과 같습니다.

  1. 이러한 전문화를 네임 스페이스에 추가하는 것이 합법적 std입니까? 나는 그것에 대해 복잡한 감정을 가지고 있습니다.

  2. 어떤 std::hash<X>::operator()버전이있는 경우 C ++ 11 표준을 준수합니까?

  3. 휴대용 방법이 있습니까?


GCC 4.7.2로, 나는 글로벌 제공했다operator==(const Key, const Key)
빅터 Lyuboslavsky

std::hash( std네임 스페이스의 다른 것과 달리)의 전문화는 Google 스타일 가이드에서 권장하지 않습니다 . 소금 한 알과 함께 가져 가십시오.
Franklin Yu

답변:


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_tostd::swap입니다.)

*) 관련된 유형 중 하나가 사용자 정의 된 한, 나는 가정한다.


3
이것이 가능하지만 일반적으로 이렇게하는 것이 좋습니다. 나는 unorder_map<eltype, hash, equality>재미있는 ADL 사업으로 누군가의 하루를 망치지 않기 위해 인스턴스화 를 선호합니다 . ( 이 주제에 대한 Pete Becker의 조언 편집 )
sehe

2
@sehe : 기본적으로 구성 할 수있는 해시 펑터가 있다면 아마도 그 이유는 무엇입니까? (단순히 member-를 구현하기 때문에 평등이 더 쉽습니다 operator==.) 내 일반적인 철학은 함수가 자연스럽고 본질적으로 유일한 "올바른"함수 (사전 사전 쌍 비교와 같은)이면에 추가한다는 것 std입니다. (순서없는 쌍 비교와 같은) 특이한 경우 컨테이너 유형에 맞게 지정합니다.
Kerrek SB 2011

3
나는 동의하지 않지만 표준에서 전문화를 표준에 추가하도록 허용 및 권장되는 표준은 어디입니까?
razeh

3
@Kerrek, 동의하지만 표준의 한 부분에 대한 장과 구절 참조를 원했습니다. 17.6.4.2.1에서 "달리 지정하지 않는 한"허용되지 않는다고 말한 삽입을 허용하는 문구를 찾았지만 4000 개 이상의 페이지 사양에서 "기타 지정된"부분을 찾을 수 없었습니다.
razeh

3
@razeh 여기에서 "선언이 사용자 정의 유형에 의존하고 전문화가 원래 템플릿에 대한 표준 라이브러리 요구 사항을 충족하고 명시 적으로 금지되지 않은 경우에만 프로그램이 표준 라이브러리 템플릿에 대한 템플릿 전문화를 네임 스페이스 std에 추가 할 수 있습니다. . " 그래서이 솔루션은 괜찮습니다.
Marek R

7

내 베팅은 unorder_map / unorder_set / ... 클래스의 Hash 템플릿 인수에 있습니다.

#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
}

물론이야

  • hashX는 전역 정적 함수일 수 있습니다.
  • 두 번째 경우에는 통과 할 수 있습니다.
    • 구식 functor 객체 ( struct Xhasher { size_t operator(const X&) const; };)
    • std::hash<X>()
    • 서명을 만족하는 바인드 표현식-

기본 생성자가없는 것을 작성할 수 있다는 점에 감사하지만, 추가 인수를 기억하기 위해 각 맵 구성을 요구하는 것은 약간의 부담입니다. 제 취향에는 너무 많은 부담이됩니다. 전문화 std::hash가 여전히 가장 좋은 방법 이지만 명시적인 템플릿 인수 는
괜찮

사용자 정의 유형을 구출하기 위해 :-) 더 진지하게, 클래스가 포함 된 단계에서 이미 손목을 때리기를 바랍니다 char*!
Kerrek SB 2011

흠 ... hash전문화가 ADL을 통해 어떻게 방해 하는지에 대한 예가 있습니까? 내 말은, 그것은 완전히 그럴듯하지만 문제 사례를 생각해내는 데 어려움을 겪습니다.
Kerrek SB 2011


a가 필요할 때까지 모두 재미 있고 게임 std::unordered_map<Whatever, Xunset>이며 Xunset해시 유형이 기본 구성 가능하지 않기 때문에 작동하지 않습니다 .
Brian Gordon

4

@Kerrek SB는 1)과 3)를 다루었습니다.

2) g ++ 및 VC10 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()이 구현에 정의 되어 있기 때문 입니다.
ildjarn 2011

@ildjarn : 명확화-시도한 전문화가 아니라 라이브러리 구현에 대해 이야기했습니다. OP가 정확히 어떤 의미인지 확실하지 않습니다.
aschepler 2011
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.