absl::bind_front와 Overload — 함수 객체 보조 도구
#한 줄 요약
absl::bind_front는 맨 앞 매개변수만 묶는 단순 binder다. std::bind의 placeholder 지옥과 implicit ref decay 문제를 피하고 lambda보다 짧다. absl::Overload는 std::variant visitor를 람다 여러 개로 한 줄에 적게 해 주는 helper다.
#동기
함수 객체 합성에는 세 가지 표준 방법이 있다.
struct Adder { int Add(int a, int b) { return a + b; } };Adder ad;
// 1. std::bind — placeholder, 복잡auto f1 = std::bind(&Adder::Add, &ad, 10, std::placeholders::_1);
// 2. lambda — 명시적이지만 길다auto f2 = [&ad](int x) { return ad.Add(10, x); };
// 3. absl::bind_front — 깔끔auto f3 = absl::bind_front(&Adder::Add, &ad, 10);std::bind는 다음 문제로 EMC++ Item 34에서 회피 권장이다.
- placeholder가 위치 기반이라 복잡.
- 인자가 implicit
std::ref/value로 결정 — 직관 어긋남. - error 메시지가 template 폭증.
bind_front는 앞에서부터 순차 binding만 지원해 의미를 단순화한다. C++20에 std::bind_front가 들어왔고 Abseil 버전은 그 polyfill이다.
#bind_front — API와 사용법
#include "absl/functional/bind_front.h"
// 멤버 함수 + thisauto handler = absl::bind_front(&Worker::Run, &worker);handler(arg1, arg2); // == worker.Run(arg1, arg2)
// 일반 함수 + 앞 인자 일부int Sub(int a, int b) { return a - b; }auto sub10 = absl::bind_front(Sub, 10);sub10(3); // 7
// pointer to data memberstruct P { int x; };auto get_x = absl::bind_front(&P::x);P p{42};get_x(p); // 42std::bind와 달리 placeholder를 쓰지 않는다. 묶지 않은 인자는 그 자리 그대로 호출 시점에 전달된다.
#lambda보다 좋은 점
// lambdaauto f = [obj = std::move(obj)](Arg a) { return obj.Do(a); };
// bind_frontauto f = absl::bind_front(&Obj::Do, std::move(obj));후자가 더 짧고 컴파일 시간도 약간 빠르다. 정적 분석기가 인자 타입을 자동 추론하기 좋다.
#내부 구현 (요약)
namespace absl {
template <typename F, typename... BoundArgs>constexpr auto bind_front(F&& f, BoundArgs&&... args) { return functional_internal::bind_front_t< std::decay_t<F>, std::decay_t<BoundArgs>...>( std::forward<F>(f), std::forward<BoundArgs>(args)...);}
// bind_front_t는 INVOKE(f, args..., CallArgs...) 형태로 호출}placeholder가 없으므로 bound args + call args가 자연스럽게 concatenate. invoke 시 std::invoke를 그대로 사용.
#bind_front vs std::bind 비교
| 항목 | std::bind | absl::bind_front (C++20 표준) |
|---|---|---|
| placeholder | _1, _2, … | 없음 |
| 인자 순서 변경 | 가능 | 불가 (앞부터만) |
std::ref 자동 처리 | × (사용자 명시) | × (사용자 명시) |
| nested bind 평탄화 | O (헷갈림) | × |
| 권장 (EMC++ Item 34) | 회피 | 권장 |
#Overload — variant visitor 도우미
std::visit는 callable 하나가 모든 alternative를 처리해야 한다. 보통 lambda 여러 개를 묶기 위해 overload trick을 쓴다.
// C++17 overload tricktemplate <typename... Ts>struct overload : Ts... { using Ts::operator()...; };template <typename... Ts>overload(Ts...) -> overload<Ts...>;
std::variant<int, std::string, double> v = "hi";std::visit(overload{ [](int i) { LOG(INFO) << "int " << i; }, [](const std::string& s){ LOG(INFO) << "str " << s; }, [](double d) { LOG(INFO) << "dbl " << d; },}, v);이 boilerplate를 Abseil이 제공한다.
#include "absl/functional/overload.h"
std::variant<int, std::string, double> v = "hi";std::visit(absl::Overload{ [](int i) { LOG(INFO) << "int " << i; }, [](const std::string& s){ LOG(INFO) << "str " << s; }, [](double d) { LOG(INFO) << "dbl " << d; },}, v);매번 trick struct를 직접 정의할 필요가 없다.
#내부 구현
namespace absl {
template <typename... T>class Overload : public T... { public: explicit Overload(T... ts) : T(std::move(ts))... {} using T::operator()...;};
template <typename... T>Overload(T...) -> Overload<T...>;
} // namespace abslTs...를 base로 다중 상속하고 using 선언으로 operator()를 한 layer에 모은다. CTAD로 호출 측에서 template 인자 명시가 필요 없다.
#핵심 활용 — exhaustive matching
using Event = std::variant<LoginEvent, ClickEvent, ErrorEvent>;
void Handle(const Event& e) { std::visit(absl::Overload{ [](const LoginEvent& l) { ... }, [](const ClickEvent& c) { ... }, [](const ErrorEvent& e) { ... }, }, e);}모든 alternative에 대응하는 lambda를 적지 않으면 컴파일 에러. 새 alternative가 추가될 때 모든 visitor가 깨지므로 컴파일러가 누락을 잡아 준다. switch + enum 패턴의 가장 큰 약점(누락)을 막는다.
#default fall-through
모든 case를 다 적기 싫을 때 generic lambda를 마지막에 둔다.
std::visit(absl::Overload{ [](const LoginEvent& l) { /* 처리 */ }, [](const auto&) { /* 그 외 */ },}, e);단, exhaustive matching의 안전망이 사라진다는 점을 인지하고 쓴다.
#std / Folly와의 비교
| 항목 | std::bind | absl::bind_front | std::bind_front (C++20) |
|---|---|---|---|
| 표준 | C++11 | — | C++20 |
| placeholder | O | × | × |
| 권장 | 회피 | 권장 | 권장 |
| 항목 | overload trick | absl::Overload | std::overload (제안) |
|---|---|---|---|
| 표준 | C++17 | — | (검토 단계) |
| boilerplate | 사용자 정의 필요 | 라이브러리 제공 | — |
folly에는 folly::partial이 유사한 역할을 한다. 인자 capture 방식 차이가 있어 직접 호환은 안 된다.
#코드 리뷰 포인트
1. std::bind 발견 → bind_front 또는 lambda
// 회피auto h = std::bind(&Worker::Run, &w, std::placeholders::_1, 42);
// Good 1 — bind_front (앞부터 묶음)auto h = absl::bind_front(&Worker::Run, &w); // 두 번째 42는 풀어둠h(arg, 42);
// Good 2 — lambda (가독성 최우선)auto h = [&w](Arg a) { return w.Run(a, 42); };std::bind는 C++20 이후 사실상 deprecated에 가깝다.
2. variant 분기 → switch보다 Overload
// 회피 — index() 분기switch (v.index()) { case 0: HandleInt(std::get<0>(v)); break; case 1: HandleStr(std::get<1>(v)); break;}
// Good — exhaustive 보장std::visit(absl::Overload{ [](int i) { HandleInt(i); }, [](const std::string& s){ HandleStr(s); },}, v);index() 분기는 새 alternative 추가 시 누락을 잡지 못한다.
3. bind_front + AnyInvocable 조합
absl::AnyInvocable<void(int)> cb = absl::bind_front(&Service::OnEvent, service);bound state는 AnyInvocable 안에 통째로 들어간다. SBO 안에 들어가는 크기에 주의.
#자주 보는 안티패턴
bind_front로 placeholder 흉내
// 회피 — 의도와 다르게 동작auto f = absl::bind_front(Sub, 10); // f(x) → Sub(10, x)auto g = absl::bind_front(Sub, _, 10); // ❌ placeholder 미지원뒤 인자를 묶고 싶으면 lambda. absl::bind_back은 C++23 std::bind_back까지 표준에 없다.
Overload에 캡처 ref가 살아남지 못함
auto Visit() { std::string greeting = "hi"; return absl::Overload{ [&](int) { LOG(INFO) << greeting; }, // greeting dangling [&](auto) { LOG(INFO) << "other"; }, };}visitor를 반환할 때는 캡처 by value.
#정리
absl::bind_front는 앞부터 순차 binding만 지원하는 단순 binder.std::bind의 placeholder·implicit ref 문제를 해결.absl::Overload는 variant visitor를 lambda 여러 개로 한 자리에 합치는 helper.- exhaustive matching이 컴파일러로 보장됨.
- 둘 다 lambda·다중 상속 trick을 표준화된 형태로 제공.
#다음 편
Part 15에서는 분산 시스템용 대용량 문자열 absl::Cord와 charconv 계열을 본다. Part 15-01 — Cord.
#관련 항목
- Part 15-01 — Cord
- Part 14-03 — function_ref / any_invocable
- Part 9-04 — absl::variant
- EMC++ Item 34 — std::bind 회피
- Folly Part 4-04 — Partial
Abseil Code Review · 73 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::variant 분석
absl::variant — std::variant의 polyfill. 타입-안전 union과 visitor 패턴.
같은 시리즈에서 이어 읽기
absl::PeriodicSampler — 적응형 샘플링·jitter 회피
absl::profiling_internal::PeriodicSampler — sampling rate를 동적으로 조정, geometric distribution으로 jitter 회피. 메모리 할당 추적·profiling 인프라의 기반.
같은 시리즈에서 이어 읽기
absl::ComputeCrc32c — 하드웨어 가속 체크섬
absl::ComputeCrc32c — SSE4.2 CRC32, ARM CRC 명령어로 가속된 CRC32C 구현. iSCSI·Btrfs·protobuf에서 표준화된 무결성 검사.
같은 시리즈에서 이어 읽기