Abseil AbslHashValue 분석
#std::hash의 불편
std::hash를 사용자 타입에 적용하려면 std namespace에 specialization 을 추가해야 한다.
// 회피 — std namespace 침범namespace std {template <>struct hash<MyPoint> { size_t operator()(const MyPoint& p) const { return hash<int>{}(p.x) ^ hash<int>{}(p.y); // 약한 해시 }};}문제 두 가지:
- 외부 namespace에 코드를 추가해야 함(어느 헤더에 둘지 골치).
int별 해시를 XOR로 섞는 직접 합성 은 충돌이 잦다.
Abseil은 멤버처럼 정의하는 friend 함수 를 통해 이 둘을 모두 해결한다.
friend 함수 → HashState chain의 흐름은 다음과 같다.
#AbslHashValue — friend ADL 기반
#include "absl/hash/hash.h"
struct MyPoint { int x, y;
template <typename H> friend H AbslHashValue(H h, const MyPoint& p) { return H::combine(std::move(h), p.x, p.y); }};
absl::flat_hash_set<MyPoint> set;set.insert({1, 2});핵심 패턴:
friend함수로 클래스 안에 정의.- 첫 인자가 HashState 템플릿(
H). H::combine(std::move(h), fields...)로 필드 chaining.- 결과 H를 반환.
이름이 AbslHashValue로 고정되어 있고 ADL로 찾아진다. namespace를 침범하지 않는다.
#왜 friend인가
friend로 클래스 안에 두면:
- 클래스와 함께 정의·유지·삭제됨.
- private 필드에 접근 가능.
- 헤더의 한 곳에 모여 검색·리뷰 쉬움.
class User {public: User(std::string n, int a) : name_(std::move(n)), age_(a) {}
template <typename H> friend H AbslHashValue(H h, const User& u) { return H::combine(std::move(h), u.name_, u.age_); // private OK }
friend bool operator==(const User& a, const User& b) { return a.name_ == b.name_ && a.age_ == b.age_; }
private: std::string name_; int age_;};해시 + 동등 비교가 함께 정의되므로 해시 컨테이너 호환성이 한 클래스에 박힌다.
#absl::Hash로 사용
직접 부르려면 absl::Hash<T>를 인스턴스화.
absl::Hash<MyPoint> hasher;size_t h = hasher(MyPoint{1, 2});absl::flat_hash_map, absl::flat_hash_set은 기본적으로 absl::Hash를 쓰므로 AbslHashValue 정의만 있으면 그대로 동작.
#std::hash와의 호환
Abseil은 std 컨테이너 호환을 위해 std::hash 특수화를 자동으로 만들지 않는다. std::unordered_map<MyPoint>를 쓰려면 여전히 std::hash 특수화가 필요하다.
// std 컨테이너 호환을 원한다면 함께 작성namespace std {template <>struct hash<MyPoint> { size_t operator()(const MyPoint& p) const { return absl::Hash<MyPoint>{}(p); // AbslHashValue로 위임 }};}std::hash가 absl::Hash를 부르도록 해 두면 해시 알고리즘이 한 곳에서 정의 된다.
#randomization — 같은 입력, 다른 출력
absl::Hash는 프로세스마다 다른 시드를 사용한다. 같은 객체도 프로세스를 재시작하면 해시값이 다르다.
// run 1absl::Hash<int>{}(42) == 0x12345678'abcdef00ULL;
// run 2 (다른 프로세스)absl::Hash<int>{}(42) == 0xfedcba98'76543210ULL;이는 hash flooding DoS 방어 다. 공격자가 충돌하는 키를 미리 계산할 수 없다. 단점: 직렬화하면 다른 머신에서 의미 없음(분산 해싱에는 별도 algorithm 필요).
#회피 패턴
// 회피 — XOR로 직접 합성template <typename H>friend H AbslHashValue(H h, const MyPoint& p) { return H::combine(std::move(h), std::hash<int>{}(p.x) ^ std::hash<int>{}(p.y)); // ❌ 약함}
// Good — H::combine에 그대로 위임template <typename H>friend H AbslHashValue(H h, const MyPoint& p) { return H::combine(std::move(h), p.x, p.y);}// 회피 — std::hash 특수화만 (absl 컨테이너 사용 불가)namespace std {template <> struct hash<MyPoint> { ... };}
// Good — AbslHashValue 우선, std 호환은 위임// 회피 — operator== 빠짐struct Bad { int x; template <typename H> friend H AbslHashValue(H h, const Bad& b) { return H::combine(std::move(h), b.x); } // ❌ operator==이 없으면 unordered 컨테이너에서 비교 실패};#작은 예시 — composite key
struct OrderKey { int64_t user_id; absl::Time when; std::string region;
template <typename H> friend H AbslHashValue(H h, const OrderKey& k) { return H::combine(std::move(h), k.user_id, k.when, k.region); }
friend bool operator==(const OrderKey& a, const OrderKey& b) { return a.user_id == b.user_id && a.when == b.when && a.region == b.region; }};
absl::flat_hash_map<OrderKey, Order> orders;orders[{123, absl::Now(), "us-east"}] = ...;absl::Time, std::string, int64_t 모두 AbslHashValue가 기본 정의되어 있으므로 combine에 그대로 넘긴다.
#정리
AbslHashValue— friend ADL 기반 hash 정의. std namespace 침범 없음.H::combine(std::move(h), fields...)이 표준 합성. 직접 XOR 회피.absl::Hash<T>가AbslHashValue를 찾아 부른다. ADL 자동.- 프로세스마다 다른 시드 → hash flooding 방어. 직렬화에는 부적합.
- std 컨테이너 호환을 원하면
std::hash<T>특수화에서absl::Hash로 위임.
#다음 장 예고
Part 10-02: HashState chaining — combine의 내부 동작.
#관련 항목
Abseil Code Review · 57 of 79
- 1 Abseil Code Review — Google production-grade C++ 라이브러리 분석
- 2 Abseil 개요 — Google이 std를 보완한 이유
- 3 Abseil 설계 철학 — std 호환과 추가 기능의 균형
- 4 Abseil 빌드와 의존성 — Bazel vs CMake
- 5 Abseil LTS vs HEAD 릴리스 모델 분석
- 6 Abseil Versioning과 ABI 호환성 정책
- 7 Abseil 매크로 — ABSL_HAVE_*·ABSL_ATTRIBUTE_*
- 8 Abseil ABSL_PREDICT_TRUE/FALSE — branch hint
- 9 absl::LogSeverity — 로그 레벨 타입
- 10 Abseil type_traits — negation·conjunction·void_t
- 11 Abseil Conformance·Policy 분석
- 12 Abseil Memory utilities 분석
- 13 Abseil raw_logging — heap-free 로깅
- 14 Abseil thread_annotations — clang TSA 통합
- 15 absl::Status — exception-free error handling
- 16 absl::StatusOr<T> — 값 또는 에러
- 17 absl status_macros — ASSIGN_OR_RETURN·RETURN_IF_ERROR
- 18 absl::Status payload — 구조화된 에러 컨텍스트
- 19 absl::Status ↔ exception 변환 패턴
- 20 absl::string_view — non-owning 문자열 참조
- 21 absl::string_view 함정 — dangling·c_str·임시 객체
- 22 absl::StrCat — 가변 인자 문자열 연결과 AlphaNum
- 23 absl::StrSplit — Delimiter·Predicate·컨테이너 변환
- 24 absl::StrJoin — 컨테이너 결합과 Formatter
- 25 absl::StrFormat — type-safe printf·FormatSpec
- 26 Abseil ASCII 함수 — locale-free 분류·대소문자 변환
- 27 Abseil Escape — CEscape·HexEscape·Base64
- 28 absl::flat_hash_map — Swiss Table 기반 hash map
- 29 absl::flat_hash_set — set 버전 Swiss Table
- 30 absl::node_hash_map — stable pointer가 필요할 때
- 31 absl::btree_map — sorted·cache-friendly B-tree
- 32 absl::FixedArray — 런타임 크기 stack 배열
- 33 absl::InlinedVector — small buffer optimization
- 34 Abseil Swiss Table internals — control byte·SIMD probing
- 35 absl::Mutex — reader-writer·fairness·deadlock 검출
- 36 absl::Mutex Conditional Critical Section — Await로 cv 없애기
- 37 absl::Notification — once-only signal
- 38 absl::BlockingCounter·Barrier — 다중 thread 조율
- 39 absl::Mutex annotations — clang thread-safety로 race를 컴파일 타임에
- 40 absl::Time·Duration 분석 — 단단한 type
- 41 absl::Time Format·Parse
- 42 absl::CivilTime 분석
- 43 absl::time_zone 분석
- 44 absl::Time mocking — 테스트 친화 시간
- 45 absl::BitGen — 모던 난수 생성기
- 46 Abseil Random Distributions — Uniform·Exponential
- 47 Abseil Mocking Random — 테스트 결정성
- 48 Abseil Random Seeding·Entropy
- 49 absl::int128·uint128 분석
- 50 absl::bits — popcount·countl_zero
- 51 absl::optional vs std::optional
- 52 absl::variant 분석
- 53 absl::span 분석
- 54 absl::any 분석
- 55 absl::compare — three-way 비교
- 56 Abseil utility — apply·in_place
- 57 Abseil AbslHashValue 분석
- 58 Abseil HashState chaining
- 59 Abseil Custom hashable 구현
- 60 Abseil LOG·VLOG·CHECK 분석
- 61 Abseil LogSink 분석
- 62 Abseil LogEntry·structured logging
- 63 Abseil Stack trace·failure_signal_handler
- 64 ABSL_FLAG 정의 분석
- 65 Abseil ParseCommandLine 동작
- 66 Abseil Flag introspection·validation
- 67 Google 스타일의 Abseil 사용 패턴
- 68 Abseil 자주 보는 anti-pattern
- 69 std → absl 마이그레이션 전략
- 70 absl::Cleanup — 함수 종료 시 실행 보장
- 71 Abseil algorithm container 확장 — c_sort·c_find_if·c_count_if
- 72 absl::function_ref와 any_invocable — 함수 객체 전달의 두 축
- 73 absl::bind_front와 Overload — 함수 객체 보조 도구
- 74 absl::Cord — 분산 시스템용 대용량 문자열
- 75 absl::from_chars·SimpleAtoi — 빠른 숫자 변환
- 76 absl::Cord vs std::string — 선택 기준과 메모리 프로파일
- 77 absl::GetStackTrace와 Symbolize — crash 시 readable stack
- 78 absl::ComputeCrc32c — 하드웨어 가속 체크섬
- 79 absl::PeriodicSampler — 적응형 샘플링·jitter 회피
관련 글
Abseil LogSink 분석
absl::LogSink — 출력 destination 커스터마이징. 파일·syslog·원격 collector·테스트 캡처.
같은 시리즈에서 이어 읽기
Abseil Custom hashable 구현
실전 사용자 타입을 hashable로 만들기 — value class, enum, pair, raw 바이트 등 흔한 패턴 정리.
같은 시리즈에서 이어 읽기
Abseil HashState chaining
H::combine의 진짜 동작 — HashState로 필드를 chain해 한 번에 좋은 분포를 얻는 방법.
같은 시리즈에서 이어 읽기