Abseil algorithm container 확장 — c_sort·c_find_if·c_count_if
#한 줄 요약
absl::c_* 함수군은 STL algorithm의 container 받는 버전이다. std::sort(v.begin(), v.end()) 대신 absl::c_sort(v)로 적는다. C++20 std::ranges의 사전 버전에 해당하며 C++17 이하 빌드에서 동일한 효용을 제공한다.
#동기
STL algorithm은 iterator 두 개를 받는다. 의도는 “container 일부에도 적용할 수 있어야 한다”이지만 실제 사용의 95%는 container 전체다.
// 회피 — boilerplate 반복std::sort(v.begin(), v.end());auto it = std::find_if(v.begin(), v.end(), pred);int n = std::count_if(v.begin(), v.end(), pred);std::transform(v.begin(), v.end(), out.begin(), fn);v.begin(), v.end()는 글자 수도 늘리고 타이포 위험도 만든다. v1.begin(), v2.end()처럼 서로 다른 container의 begin/end를 섞는 버그가 실제로 발생한다.
absl::c_*은 container 한 개를 받아 begin/end를 자동으로 풀어 준다.
// Goodabsl::c_sort(v);auto it = absl::c_find_if(v, pred);int n = absl::c_count_if(v, pred);absl::c_transform(v, out.begin(), fn);#API와 사용법
대부분의 <algorithm> 함수에 c_ 접두사 버전이 있다.
#include "absl/algorithm/container.h"
// 정렬absl::c_sort(v);absl::c_sort(v, std::greater<>());absl::c_stable_sort(v);
// 검색auto it = absl::c_find(v, target);auto it2 = absl::c_find_if(v, [](int x) { return x > 10; });bool yes = absl::c_any_of(v, IsPositive);bool all = absl::c_all_of(v, IsPositive);bool none = absl::c_none_of(v, IsNegative);
// 카운트int n = absl::c_count(v, target);int m = absl::c_count_if(v, IsPositive);
// 변형absl::c_transform(v, out.begin(), [](int x) { return x * 2; });absl::c_copy(src, std::back_inserter(dst));absl::c_copy_if(src, std::back_inserter(dst), pred);absl::c_fill(v, 0);
// 집계int sum = absl::c_accumulate(v, 0);int prod = absl::c_accumulate(v, 1, std::multiplies<>());
// set 연산std::vector<int> out;absl::c_set_intersection(a, b, std::back_inserter(out));absl::c_set_union(a, b, std::back_inserter(out));
// 순서·정렬 확인bool sorted = absl::c_is_sorted(v);absl::c_reverse(v);absl::c_unique_copy(v, std::back_inserter(out)); // 인접 중복 제거 (in-place c_unique는 abseil이 의도적으로 생략)#내부 구현
absl/algorithm/container.h의 구현은 얇은 어댑터다.
namespace absl {
template <typename C>void c_sort(C& c) { std::sort(container_algorithm_internal::c_begin(c), container_algorithm_internal::c_end(c));}
template <typename C, typename Compare>void c_sort(C& c, Compare&& comp) { std::sort(container_algorithm_internal::c_begin(c), container_algorithm_internal::c_end(c), std::forward<Compare>(comp));}
template <typename C, typename Pred>typename container_algorithm_internal::ContainerIter<C> c_find_if(C& c, Pred&& pred) { return std::find_if(container_algorithm_internal::c_begin(c), container_algorithm_internal::c_end(c), std::forward<Pred>(pred));}
} // namespace abslc_begin/c_end는 std::begin/std::end의 ADL-safe wrapper다. C-style array도 그대로 받는다.
int arr[5] = {3, 1, 4, 1, 5};absl::c_sort(arr); // OK — C 배열도 동작오버헤드는 없다. inline 호출 한 번이 컴파일러에 의해 사라진다.
#std::ranges와의 비교
C++20 std::ranges는 동일한 컨셉을 표준으로 제공한다.
// C++20 std::rangesstd::ranges::sort(v);auto it = std::ranges::find_if(v, pred);int n = std::ranges::count_if(v, pred);
// abseilabsl::c_sort(v);auto it = absl::c_find_if(v, pred);int n = absl::c_count_if(v, pred);| 항목 | absl::c_* | std::ranges |
|---|---|---|
| C++ 표준 | — (C++14+ 동작) | C++20 |
projection (&Foo::id) | × | O |
| sentinel(불일치 begin/end) | × | O |
view·composition (v | filter) | × | O |
| iterator concept | duck typing | 엄격 |
| 컴파일 오류 메시지 | 평이 | template + concept 폭발 |
absl::c_*은 C++17 이하에서 ranges의 가장 일반적인 사용 케이스를 메운다. C++20 빌드면 표준 ranges를 우선 검토하되, projection이나 view composition을 쓰지 않는다면 차이가 거의 없다. Google 내부 코드는 일관성 차원에서 absl::c_*을 유지한다.
#코드 리뷰 포인트
1. v.begin(), v.end() 보면 자동 치환 후보
// beforestd::sort(v.begin(), v.end());// afterabsl::c_sort(v);이 패턴은 거의 mechanical refactor다. C++20 ranges를 쓸 환경이면 그쪽으로 바로 가도 좋다.
2. iterator를 명시적으로 들고 다닐 때는 그대로 두자
auto mid = v.begin() + v.size() / 2;std::sort(v.begin(), mid); // 부분 정렬 — c_sort로 줄일 수 없다c_*은 container 전체용이다. 일부분만 다루면 STL을 그대로 쓴다.
3. associative container 주의
absl::flat_hash_map<int, int> m;absl::c_sort(m); // 컴파일 에러 — hash map은 정렬 불가c_sort/c_unique는 random-access iterator를 요구한다. 컴파일러가 잡아 주지만 에러 메시지가 길다.
4. projection 필요하면 ranges
struct User { int id; std::string name; };std::vector<User> users;
// c_*에서는 lambda 필요absl::c_sort(users, [](const User& a, const User& b) { return a.id < b.id; });
// ranges는 projection 한 줄std::ranges::sort(users, {}, &User::id);projection이 자주 등장하는 코드라면 ranges가 더 짧다.
#자주 보는 안티패턴
서로 다른 container 섞기
// 회피 — STL이라면 컴파일은 되지만 UB 가능std::sort(v1.begin(), v2.end());c_*은 container 한 개만 받으므로 이 실수가 원천 차단된다. 그래서 c_*을 선호하는 이유 중 하나다.
lambda 안에서 it++ 오용
// 회피 — for 루프를 algorithm으로 위장absl::c_for_each(v, [&](int x) { if (x == target) { /* break를 흉내내려 시도 */ }});c_for_each는 break가 없다. early exit이 필요하면 c_any_of / c_find_if가 옳다.
c_sort 이후 binary_search 누락
absl::c_sort(v);// 회피 — linear searchif (absl::c_find(v, x) != v.end()) { ... }
// Good — sorted vector면 binaryif (absl::c_binary_search(v, x)) { ... }정렬 비용을 들였으면 검색도 binary로 받는다.
#정리
absl::c_*은 container 한 개를 받는 algorithm wrapper.begin/end반복을 제거하고 다른 container 섞기 실수를 차단한다.- 구현은 inline 어댑터로 런타임 오버헤드 0.
- C++20 ranges와 효용은 겹치지만 projection·view·sentinel은 ranges만의 영역.
- C++17 이하 또는 Google 일관성을 유지할 코드베이스에서 표준 도구.
#다음 편
Part 14-03 — function_ref와 any_invocable에서 함수 객체 전달의 두 축을 본다.
#관련 항목
Abseil Code Review · 71 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::InlinedVector — small buffer optimization
Part 5-06: absl::InlinedVector — std::vector + 작으면 stack, 커지면 heap. SBO 패턴의 표준 도구.
같은 시리즈에서 이어 읽기
absl::FixedArray — 런타임 크기 stack 배열
Part 5-05: absl::FixedArray — 런타임 결정 크기지만 작으면 stack, 크면 heap. VLA의 안전한 대체.
같은 시리즈에서 이어 읽기
absl::btree_map — sorted·cache-friendly B-tree
Part 5-04: absl::btree_map — std::map(red-black tree)의 B-tree 대체, cache locality와 메모리 효율, sorted 컨테이너의 새 기준.
같은 시리즈에서 이어 읽기