absl::variant 분석
#std와의 관계
absl::variant는 absl::optional과 같은 방식으로 C++17 이상에서는 std의 별칭이다.
#include "absl/types/variant.h"
absl::variant<int, std::string, double> v = 42;v = std::string("hello");v = 3.14;C++14/Abseil-polyfill 시기에 작성된 코드도 그대로 동작한다.
#기본 사용
absl::variant<int, std::string> v = 10;
// 현재 들어 있는 인덱스size_t i = v.index(); // 0
// 타입 검사if (absl::holds_alternative<int>(v)) { int x = absl::get<int>(v);}
// 인덱스 기반double d = absl::get<0>(v); // ❌ 타입 안 맞음 — bad_variant_access
// 안전 getterif (const int* p = absl::get_if<int>(&v)) { use(*p);}#Visitor 패턴
여러 타입을 한 번에 처리하려면 visitor.
absl::variant<int, std::string, double> v = ...;
std::string s = absl::visit([](const auto& val) -> std::string { if constexpr (std::is_same_v<std::decay_t<decltype(val)>, int>) { return absl::StrCat("int=", val); } else if constexpr (std::is_same_v<std::decay_t<decltype(val)>, std::string>) { return absl::StrCat("str=", val); } else { return absl::StrCat("dbl=", val); }}, v);C++17 if constexpr + lambda 조합이 표준 visitor 작성법이다. 더 깔끔하게는 overloaded 패턴:
template <typename... Ts>struct overloaded : Ts... { using Ts::operator()...; };template <typename... Ts> overloaded(Ts...) -> overloaded<Ts...>;
absl::variant<int, std::string, double> v = ...;std::string s = absl::visit(overloaded{ [](int n) { return absl::StrCat("int=", n); }, [](const std::string& s){ return absl::StrCat("str=", s); }, [](double d) { return absl::StrCat("dbl=", d); },}, v);가독성이 크게 좋다.
#언제 variant를 쓰나
여러 동등한 의미의 타입이 한 슬롯에 들어갈 때.
// API 응답 — 성공 or 에러 메시지 or rate-limitedabsl::variant<Response, ApiError, RateLimited> result = Call();
// 토큰 — 숫자, 식별자, 문자열 리터럴absl::variant<int64_t, std::string, double> Token;
// 이벤트 — 각각 다른 페이로드struct ClickEvent { int x, y; };struct KeyEvent { int code; };struct ScrollEvent { double delta; };absl::variant<ClickEvent, KeyEvent, ScrollEvent> Event;상속 + virtual 함수 대안으로 closed set에 적합. 새 타입을 추가할 때 모든 visitor가 컴파일 에러로 알려준다(빠진 case 발견).
#monostate — 비어 있는 상태
variant는 기본 생성 시 첫 번째 타입의 기본값을 가진다. “비어 있음” 상태가 필요하면 monostate를 첫 번째로.
absl::variant<absl::monostate, Request, Response> conn;// 기본 — monostate (아직 어떤 메시지도 안 옴)#get vs get_if
absl::variant<int, std::string> v = "hello";
// get<T> — 잘못된 타입이면 throwauto s = absl::get<std::string>(v); // OKauto n = absl::get<int>(v); // ❌ throw absl::bad_variant_access
// get_if<T> — 잘못된 타입이면 nullptrif (auto* p = absl::get_if<std::string>(&v)) { use(*p);}코드 리뷰에서 get을 무방비로 부르는 것은 흔한 지적이다. 항상 holds_alternative 가드 또는 get_if 사용.
#비교
같은 인덱스의 같은 타입이면 그 타입의 비교. 인덱스가 다르면 인덱스 순서.
absl::variant<int, std::string> a = 5;absl::variant<int, std::string> b = std::string("hello");
a < b; // true — int(index 0) < string(index 1)이 의미가 헷갈리므로 변형 비교는 가급적 명시적 visit으로 한다.
#variant의 비용
| 측면 | 비용 |
|---|---|
| sizeof | max(sizeof(Ts)) + index (정렬 패딩 포함) |
| 접근 | tag check + branch — branchless 불가 |
| visit | constexpr-resolved jump table (대부분 분기 한 번) |
| construct/destroy | 활성 타입의 ctor/dtor 호출 |
union 직접 사용보다 살짝 무겁지만(인덱스 1바이트 + 동적 분기) type safety가 그 비용을 정당화한다.
#회피 패턴
// 회피 — variant<int, int> 같은 모호한 alternativeabsl::variant<int, int> v; // ❌ 컴파일 에러는 아니지만 의미 불분명
// Good — 별칭으로 의도 분리struct UserId { int v; };struct OrderId { int v; };absl::variant<UserId, OrderId> v;// 회피 — visitor에 모든 alternative 안 다룸absl::visit(overloaded{ [](int n) { /* ... */ }, // string은? — overload mismatch → 컴파일 에러로 잡힘 (good!)}, v);overloaded 패턴의 장점이 여기서 드러난다. 새 alternative를 추가하면 컴파일이 깨져서 모든 호출 지점에서 처리하게 강제한다.
#작은 예시 — 토큰화 결과
struct Number { double value; };struct Identifier { std::string name; };struct StringLit { std::string text; };struct Punct { char ch; };
using Token = absl::variant<Number, Identifier, StringLit, Punct>;
std::string Print(const Token& t) { return absl::visit(overloaded{ [](const Number& n) { return absl::StrCat("NUM(", n.value, ")"); }, [](const Identifier& i){ return absl::StrCat("ID(", i.name, ")"); }, [](const StringLit& s) { return absl::StrCat("STR(\"", s.text, "\")"); }, [](const Punct& p) { return absl::StrCat("'", std::string(1, p.ch), "'"); }, }, t);}#정리
absl::variant는 C++17 이상에서std::variant의 별칭.- type-safe union —
holds_alternative/get_if/visit로 안전 접근. overloaded{...} + visit패턴이 visitor 작성의 표준.- closed set의 다형성에 적합 — 상속 + virtual의 가벼운 대안.
get<T>는 throw하므로 무방비 호출 회피,get_if/holds_alternative우선.absl::monostate로 비어 있는 상태 표현.
#다음 장 예고
Part 9-05: absl::span — 연속 메모리 view.
#관련 항목
Abseil Code Review · 52 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 회피
관련 글
absl::bind_front와 Overload — 함수 객체 보조 도구
absl::bind_front — std::bind의 후속, 정적 분석 친화. absl::Overload — variant visitor 작성을 줄이는 도우미.
같은 시리즈에서 이어 읽기
absl::any 분석
absl::any — 임의 타입을 담는 type-erased 컨테이너. variant와 언제 어떻게 다른가.
같은 시리즈에서 이어 읽기
absl::span 분석
absl::Span — 연속 메모리의 non-owning view. std::span의 친척이자 더 일찍 도착한 polyfill.
같은 시리즈에서 이어 읽기
이 글을 참조하는 글 (7)
- absl::bind_front와 Overload — 함수 객체 보조 도구 — Abseil Code Review
- absl::function_ref와 any_invocable — 함수 객체 전달의 두 축 — Abseil Code Review
- Abseil utility — apply·in_place — Abseil Code Review
- absl::compare — three-way 비교 — Abseil Code Review
- absl::any 분석 — Abseil Code Review
- absl::optional vs std::optional — Abseil Code Review
- Abseil type_traits — negation·conjunction·void_t — Abseil Code Review