utility_hash
utility_hash (해시 함수 객체)
std::hash는 주어진 키 타입에 대한 해시 값을 계산하는 함수 객체를 생성하는 클래스 템플릿이에요. std::unordered_set, std::unordered_map 같은 무순서 연관 컨테이너가 기본 해시 함수로 사용해요. 이 페이지에서는 std::hash의 정의, 활성화 조건, 표준 라이브러리에서 제공하는 특수화 목록을 확인할 수 있어요.
출처: cppreference
본문
정의
다음 헤더에 정의되어 있어요:
| 헤더 | |
|---|---|
<bitset> |
|
<coroutine> |
|
<chrono> |
(C++26부터) |
<filesystem> |
|
<functional> |
|
<memory> |
|
<optional> |
|
<stacktrace> |
|
<string> |
|
<string_view> |
|
<system_error> |
|
<text_encoding> |
|
<thread> |
|
<typeindex> |
|
<variant> |
|
<vector> |
template < class Key > struct hash;
(C++11부터)
무순서 연관 컨테이너 std::unordered_set, std::unordered_multiset, std::unordered_map, std::unordered_multimap은 기본 해시 함수로 std::hash 템플릿의 특수화를 사용해요.
활성화 조건
어떤 타입 Key에 대해 각 특수화 std::hash<Key>는 활성화(enable)되거나 비활성화(disable)돼요.
- 프로그램이나 사용자가
std::hash<Key>를 제공하지 않으면 비활성화돼요. - 그렇지 않으면 다음 조건을 모두 만족할 때
std::hash<Key>가 활성화돼요:- 다음 요구 사항을 모두 만족해야 해요:
Hash(Key를 함수 호출 인자 타입으로 사용),DefaultConstructible,CopyAssignable,Swappable. - 다음 값들이 주어졌을 때:
h는std::hash<Key>타입의 객체,k1과k2는Key타입의 객체. 다음 요구 사항을 모두 만족해야 해요:k1 == k2가true이면h(k1) == h(k2)도true여야 해요.std::hash<Key>가 프로그램 정의 특수화가 아니라면h(k1)은 예외를 던지지 않아요.
- 다음 요구 사항을 모두 만족해야 해요:
- 그 외에는
std::hash<Key>는 비활성화돼요.
비활성화된 특수화는 Hash 요구 사항도, FunctionObject 요구 사항도 만족하지 않으며, 다음 값들이 모두 false가 돼요:
std::is_default_constructible<std::hash<Key>>::valuestd::is_copy_constructible<std::hash<Key>>::valuestd::is_move_constructible<std::hash<Key>>::valuestd::is_copy_assignable<std::hash<Key>>::valuestd::is_move_assignable<std::hash<Key>>::value
즉, 존재는 하지만 사용할 수 없어요.
중첩 타입
| 타입 | 정의 |
|---|---|
argument_type (C++17부터 deprecated) |
Key |
result_type (C++17부터 deprecated) |
std::size_t |
(C++20까지)
멤버 함수
| 멤버 함수 | 설명 |
|---|---|
(constructor) |
해시 함수 객체를 생성해요 (공개 멤버 함수) |
operator() |
인자의 해시 값을 계산해요 (공개 멤버 함수) |
표준 라이브러리 특수화
템플릿 std::hash를 선언하는 각 헤더는 다음 타입들에 대해 활성화된 std::hash 특수화도 제공해요:
- cv 한정이 없는 모든 산술 타입 (cv-unqualified arithmetic types)
- cv 한정이 없는 모든 열거형 타입
- cv 한정이 없는 모든 포인터 타입
std::nullptr_t
그 외에도 일부 헤더는 라이브러리 타입에 대한 다른 활성화된 std::hash 특수화를 제공해요 (아래 참고).
표준 라이브러리가 제공하는 모든 std::hash 특수화 중 다음을 제외하면 모든 멤버 함수가 noexcept예요:
std::hash<std::optional>std::hash<std::variant>std::hash<std::unique_ptr>std::hash<std::indirect>(C++26부터)std::hash<std::chrono::duration>(C++26부터)std::hash<std::chrono::time_point>(C++26부터)std::hash<std::chrono::zoned_time>(C++26부터)
(C++17부터)
라이브러리 타입 특수화
언어 지원 라이브러리
| 특수화 | 설명 |
|---|---|
std::hash<std::coroutine_handle> (C++20) |
std::coroutine_handle에 대한 해시 지원 (클래스 템플릿 특수화) |
진단 라이브러리
| 특수화 | 설명 |
|---|---|
std::hash<std::error_code> (C++11) |
std::error_code에 대한 해시 지원 |
std::hash<std::error_condition> (C++17) |
std::error_condition에 대한 해시 지원 |
std::hash<std::type_index> (C++11) |
std::type_index에 대한 해시 지원 |
std::hash<std::stacktrace_entry> (C++23) |
std::stacktrace_entry에 대한 해시 지원 |
std::hash<std::basic_stacktrace> (C++23) |
std::basic_stacktrace에 대한 해시 지원 |
메모리 관리 라이브러리
| 특수화 | 설명 |
|---|---|
std::hash<std::unique_ptr> (C++11) |
std::unique_ptr에 대한 해시 지원 |
std::hash<std::shared_ptr> (C++11) |
std::shared_ptr에 대한 해시 지원 |
std::hash<std::indirect> (C++26) |
std::indirect에 대한 해시 지원 |
일반 유틸리티 라이브러리
| 특수화 | 설명 |
|---|---|
std::hash<std::optional> (C++17) |
std::optional에 대한 해시 지원 |
std::hash<std::variant> (C++17) |
std::variant에 대한 해시 지원 |
std::hash<std::monostate> (C++17) |
std::monostate에 대한 해시 지원 |
std::hash<std::bitset> (C++11) |
std::bitset에 대한 해시 지원 |
컨테이너 라이브러리
| 특수화 | 설명 |
|---|---|
std::hash<std::vector<bool>> (C++11) |
std::vector<bool>에 대한 해시 지원 |
문자열 라이브러리
| 특수화 | 설명 |
|---|---|
std::hash<std::basic_string> (C++11) |
문자열에 대한 해시 지원 |
std::hash<std::string_view> (C++17) |
string view에 대한 해시 지원 |
std::hash<std::wstring_view> (C++17) |
|
std::hash<std::u8string_view> (C++20) |
|
std::hash<std::u16string_view> (C++17) |
|
std::hash<std::u32string_view> (C++17) |
텍스트 처리 라이브러리
| 특수화 | 설명 |
|---|---|
std::hash<std::text_encoding> (C++26) |
std::text_encoding에 대한 해시 지원 |
시간 라이브러리
| 특수화 | 설명 |
|---|---|
std::hash<std::chrono::duration> (C++26) |
std::chrono::duration에 대한 해시 지원 |
std::hash<std::chrono::time_point> (C++26) |
std::chrono::time_point에 대한 해시 지원 |
std::hash<std::chrono::day> (C++26) |
std::chrono::day에 대한 해시 지원 |
std::hash<std::chrono::month> (C++26) |
std::chrono::month에 대한 해시 지원 |
std::hash<std::chrono::year> (C++26) |
std::chrono::year에 대한 해시 지원 |
std::hash<std::chrono::weekday> (C++26) |
std::chrono::weekday에 대한 해시 지원 |
std::hash<std::chrono::weekday_indexed> (C++26) |
std::chrono::weekday_indexed에 대한 해시 지원 |
std::hash<std::chrono::weekday_last> (C++26) |
std::chrono::weekday_last에 대한 해시 지원 |
std::hash<std::chrono::month_day> (C++26) |
std::chrono::month_day에 대한 해시 지원 |
std::hash<std::chrono::month_day_last> (C++26) |
std::chrono::month_day_last에 대한 해시 지원 |
std::hash<std::chrono::month_weekday> (C++26) |
std::chrono::month_weekday에 대한 해시 지원 |
std::hash<std::chrono::month_weekday_last> (C++26) |
std::chrono::month_weekday_last에 대한 해시 지원 |
std::hash<std::chrono::year_month> (C++26) |
std::chrono::year_month에 대한 해시 지원 |
std::hash<std::chrono::year_month_day> (C++26) |
std::chrono::year_month_day에 대한 해시 지원 |
std::hash<std::chrono::year_month_day_last> (C++26) |
std::chrono::year_month_day_last에 대한 해시 지원 |
std::hash<std::chrono::year_month_weekday> (C++26) |
std::chrono::year_month_weekday에 대한 해시 지원 |
std::hash<std::chrono::year_month_weekday_last> (C++26) |
std::chrono::year_month_weekday_last에 대한 해시 지원 |
std::hash<std::chrono::zoned_time> (C++26) |
std::chrono::zoned_time에 대한 해시 지원 |
std::hash<std::chrono::leap_second> (C++26) |
std::chrono::leap_second에 대한 해시 지원 |
입출력 라이브러리
| 특수화 | 설명 |
|---|---|
std::hash<std::filesystem::path> (C++17) |
std::filesystem::path에 대한 해시 지원 |
동시성 지원 라이브러리
| 특수화 | 설명 |
|---|---|
std::hash<std::thread::id> (C++11) |
std::thread::id에 대한 해시 지원 |
참고
실제 해시 함수는 구현에 따라 달라요. 위에서 명시한 기준 외에 다른 품질 기준을 충족할 필요는 없어요. 특히 일부 구현은 정수를 그 자체로 매핑하는 단순(항등) 해시 함수를 사용하기도 해요. 즉, 이런 해시 함수는 무순서 연관 컨테이너와 함께 사용하도록 설계되었지, 예를 들어 암호화 해시로는 적합하지 않아요.
해시 함수는 프로그램의 단일 실행 안에서 같은 입력에 대해 같은 결과를 내기만 하면 돼요. 이 덕분에 충돌 서비스 거부 공격을 막기 위한 솔트(salted) 해시를 사용할 수 있어요.
C 문자열에 대한 특수화는 없어요. std::hash<const char*>는 포인터 값(메모리 주소)의 해시를 생성하며, 문자 배열의 내용을 검사하지 않아요.
std::pair와 표준 컨테이너 타입에 대한 추가 특수화와 해시를 결합하는 유틸리티 함수는 boost::hash에서 사용할 수 있어요.
예제
#include <cstddef>
#include <functional>
#include <iomanip>
#include <iostream>
#include <string>
#include <unordered_set>
struct S
{
std::string first_name;
std::string last_name;
bool operator==(const S&) const = default; // since C++20
};
// Before C++20.
// bool operator==(const S& lhs, const S& rhs)
// {
// return lhs.first_name == rhs.first_name && lhs.last_name == rhs.last_name;
// }
// Custom hash can be a standalone function object.
struct MyHash
{
std::size_t operator()(const S& s) const noexcept
{
std::size_t h1 = std::hash<std::string>{}(s.first_name);
std::size_t h2 = std::hash<std::string>{}(s.last_name);
return h1 ^ (h2 << 1); // or use boost::hash_combine
}
};
// Custom specialization of std::hash can be injected in namespace std.
template<>
struct std::hash<S>
{
std::size_t operator()(const S& s) const noexcept
{
std::size_t h1 = std::hash<std::string>{}(s.first_name);
std::size_t h2 = std::hash<std::string>{}(s.last_name);
return h1 ^ (h2 << 1); // or use boost::hash_combine
}
};
int main()
{
std::string str = "Meet the new boss...";
std::size_t str_hash = std::hash<std::string>{}(str);
std::cout << "hash(" << std::quoted(str) << ") =\t" << str_hash << '\n';
S obj = {"Hubert", "Farnsworth"};
// Using the standalone function object.
std::cout << "hash(" << std::quoted(obj.first_name) << ", "
<< std::quoted(obj.last_name) << ") =\t"
<< MyHash{}(obj) << " (using MyHash) or\n\t\t\t\t"
<< std::hash<S>{}(obj) << " (using injected specialization)\n";
// Custom hash makes it possible to use custom types in unordered containers.
// The example will use the injected std::hash<S> specialization above,
// to use MyHash instead, pass it as a second template argument.
std::unordered_set<S> names = {obj, {"Bender", "Rodriguez"}, {"Turanga", "Leela"}};
for (auto const& s: names)
std::cout << std::quoted(s.first_name) << ' '
<< std::quoted(s.last_name) << '\n';
}
가능한 출력:
hash("Meet the new boss...") = 10656026664466977650
hash("Hubert", "Farnsworth") = 12922914235676820612 (using MyHash) or
12922914235676820612 (using injected specialization)
"Bender" "Rodriguez"
"Turanga" "Leela"
"Hubert" "Farnsworth"
결함 보고
다음 동작을 변경하는 결함 보고서는 이전에 발표된 C++ 표준에 소급 적용되었어요.
| DR | 적용 대상 | 발표된 동작 | 올바른 동작 |
|---|---|---|---|
| LWG 2119 | C++11 | 확장 정수 타입에 대한 특수화가 없었음 | 제공됨 |
| LWG 2148 | C++11 | 열거형에 대한 특수화가 없었음 | 제공됨 |
| LWG 2543 | C++11 | std::hash가 SFINAE 친화적이지 않을 수 있었음 |
SFINAE 친화적으로 변경됨 |
| LWG 2817 | C++11 | std::nullptr_t에 대한 특수화가 없었음 |
제공됨 |