2013-07-07 5 views
7

과부하 기능 해결 중에 수행 된 암시 적 유형 변환을 피하기 위해 std::hash이 템플릿 구조체로 정의되어 있다고 생각합니다. 말하기에 올바른 것인가? 내 말은std :: hash가 오버로드 된 함수가 아닌 이유는 무엇입니까?

, 나는

std::string s; 
size_t hash = std::hash(s); 

대신

std::string s; 
size_t hash = std::hash<std::string>()(s); 

의 작성 선호하지만 표준위원회는 두 번째 옵션을 선택한 이유에 대한 이유가 같은데요.

수정 : 고정 된 두 번째 코드 조각.

+0

@RiaD 감사합니다. 갑자기, 그것은 더 어색함을 느낀다. –

답변

10

부분적으로 함수 템플릿을 특수화하므로 사용자 정의 템플릿 클래스의 경우 함수 인 경우 std::hash을 특수화 할 수 없습니다. 템플릿은 std 네임 스페이스에서만 전문화 할 수 있지만 과부하가 아니므로 템플릿 기반 클래스의 사용자는 std::unordered_map<MyClass<whatever>, MyOtherClass>을 만들 수 없으므로 std::unordered_map<MyClass<whatever>, MyOtherClass, ???>을 선택해야합니다. 그래서 functor가 해결책입니다.

namespace std 
{ 
    template<typename T> 
    struct hash<MyVector<T>> 
    { 
     size_t operator()(const MyVector<T>& v) 
     { 
      //return hash from here 
     } 
    }; 
} 

표준 라이브러리의 다른 방법은 기본적으로 회원 .hash(), 표준 해시 다른 경우 경우를 선택하는 몇 가지 SFINAE 템플릿 트릭을 사용하는 것입니다 만, 사용, 특히 대부분의 경우 (인터페이스를 갱신 할 수 없습니다 내 경험에

//somewhere in std::unordered_map 
using std::hash; 
size_t h = hash(key); 

는 ADL이 까다 롭습니다하고 모두가 코너 케이스에 대한 기억 : 다른 대안 std::swap 같은 것

타사 코드) ADL과 (트릭)를 수행합니다. 게다가 여기에있는 펑터의 장점은 템플릿 매개 변수로 사용할 수 있다는 사실입니다. 기본값이 틀렸다고 생각하면 템플릿 (예 : std::unordered_map<A, B, specialized_hash<A>>)에 다른 펑터를 플러그인하면됩니다.

의견에서

:

하지만 당신은 표준 : 스왑에 대해 좀 더 정교한 수 있을까? 아직 거기에있어 C++ 11에서 그리고 그것은 사용자 정의 유형에 문제가 있습니까? 왜 은 더 많은 것을 만들기보다는 STL에서 많은 개념을 유지해야 하는가? ?

std::hash에서 :

  • 그것은 가능성이, 예를 들어,

std::swapstd::hash 사이에 약간의 차이가있다 std::string 클래스 작성자가 정의한 해시가 충분하지 않을 수 있습니다. 즉, 너무 일반적이어서 해시 맵에 한 종류의 문자열 만 넣을 수 있음을 보장 할 수 있으므로 해시 함수를 더 빨리 또는/충돌이 적습니다.

  • 은 그것의 :들이 generic 여기에 훨씬 덜 중요하다, 그래서
  • 은 가능 대부분의 경우
  • 당신이 더 나은 해시 std::swap에서

을 만들기 위해 서로 다른 목적을 위해 해시 많은 종류가 있습니다 자신의 스왑 함수를 원할 것 같지는 않지만 아마도 복사 생성자를 호출하는 generic std::swap이 아닌이 클래스에 해당하는 특정 클래스를 사용하려고 할 것입니다.

  • 대부분의 경우 클래스 내부에 대한 지식이 필요하므로 스왑 함수를 만들조차 할 수 없습니다 (예 : std::vector은 포인터가 비공개 필드로 숨겨진 동적 배열로 구현 될 수 있으므로 혼자서 교환하는 것이 아니라, 그 방법으로 구현 된 사실조차도 보장되지는 않습니다.)
  • 하나의 스왑이 존재합니다.
  • 실제로는 std::swap에 문제가 있습니다. 표준 컨테이너는 swap 멤버 함수를 제공하고 std::swap은 특수화 될 수 있지만 (템플릿이없는 클래스에만 해당) 스왑은 ADL에서 찾을 수있는 무료 함수로 정의 할 수 있습니다. 스왑을 어떻게 제공해야합니까? IMO는 혼란 스럽지만 실제로는 std::swap이 기능하고 std::hash은 functor입니다.
  • 왜 STL이 일치하지 않습니까? 여기서만 추측 할 수 있지만 STL이 일치하지 않는 주된 이유는 (a) bawkward 호환성이며 (b) C++ 또한 매우 일관성이 없습니다.

    +0

    @milleniumbug :'std' 네임 스페이스에 정의 된 함수를 오버로드 할 수 없다고 말하는 표준 참조가 있습니까? 나는 ADL이 당신의 해쉬 함수를 찾을 것이라고 생각했을 것이다. –

    +0

    @ user1131467'17.4.3.1/1 C++ 프로그램이 별도로 지정하지 않는 한 선언이나 정의를 네임 스페이스 std 또는 네임 스페이스 std에 추가하는 것은 정의되지 않았습니다.프로그램은 표준 라이브러리 템플릿에 대한 템플릿 전문화를 namespace std에 추가 할 수 있습니다. 표준 라이브러리의 이러한 특수화 (전체 또는 부분)는 선언이 외부 링키지의 사용자 정의 이름에 의존하고 템플릿 전문화가 원래 템플릿의 표준 라이브러리 요구 사항을 충족시키지 않는 한 정의되지 않은 동작을 발생시킵니다. '(http : //stackoverflow.com/a/109613/1012936) – milleniumbug

    +1

    네 네임 스페이스에서'hash (MyType)'을 정의한 다음'std :: unordered_map '을'std '네임 스페이스의 경우, std :: unordered_map 는 ADL 때문에 해시 함수를 사용하게됩니다 (이것의 주요 포인트 중 하나입니다). 표준 라이브러리는 방금 설명한 오버로드 된 함수가 아닌 템플릿 펑터를 사용합니다. –

    4

    한 가지 가능한 이유는이 방법으로는 BTW (이 방법

    또는

    template<typename T, typename... Args> 
    auto hasher(Args&&... args) -> whatever { 
        return std::hash<T>(std::forward<Args>(args)...)(//maybe some &'s skipped 
    } 
    
    작동 기능을 만들 수 있습니다 변경 옵션

    template <typrname T, typename = std::hash<T>...> 
    class unordered_set; 
    

    에 의해 기본적으로 템플릿에서 사용하기 쉽게한다는 것입니다 감지 유형 허용)

    template<typename T, typename... Args> 
    auto hasher(T t) -> whatever { 
        return std::hash<T>()(t); 
    } 
    
    +0

    'struct std :: hasher {size_t 연산자 (T && x) {std :: hash (std :: forward (x))}}; 또는 그 효과가 있습니까? 평등은 비슷한 방식으로 이루어지며'operator =='은 오버로드되고'std :: equal_to '는 (기본적으로)'operator =='에 위임합니다. – delnan

    +0

    가능 합니다만, '해시'는 사용하기가 쉽지 않으므로 중요하지 않다고 생각합니다 (일반적으로 세트와 같은 데이터 구조는 일반적으로 (특히 일반적인 방식으로) 필요합니다. 광고가 그렇게 중요한 이유가 아닌 경우 더 많은 간접적 인 지시 (Indirection) 레벨 == (C에서) : 그것은 std :: equal_to 전에 소개되었고 equal_to는 이미 존재하는 feature에 추가되었다. – RiaD

    +0

    OP가 보여 주었기 때문에, 당신이 당신은 표준 라이브러리를 보았습니까? – delnan

    관련 문제