absl::span 분석
#span이 푸는 문제
배열·벡터·서브 슬라이스를 받는 함수의 시그니처는 모든 입력 형태를 받기 어렵다.
// 회피 — 여러 오버로드void Process(const std::vector<int>& v);void Process(const int* p, size_t n);void Process(std::initializer_list<int> il);absl::Span은 연속 메모리의 view로 이걸 한 줄로 묶는다.
#include "absl/types/span.h"
void Process(absl::Span<const int> s);
Process(std::vector<int>{1, 2, 3});int arr[] = {1, 2, 3};Process(arr);Process({1, 2, 3});Process(absl::MakeSpan(arr, 2)); // 슬라이스#std::span과의 관계
C++20 std::span이 표준에 들어왔지만 두 가지 차이가 있다.
| 측면 | absl::Span | std::span |
|---|---|---|
| extent | 항상 dynamic (런타임 크기) | static or dynamic |
| 가용성 | C++11 이상 | C++20 이상 |
| 인터페이스 | std 호환 (size, data, begin/end) | 동일 |
std::span<int, 5> 같은 static extent는 absl::Span에 없다. fixed-size 배열을 컴파일 타임에 검증하는 용도가 아니면 사실상 같은 도구다.
#생성
absl::MakeSpan 함수 헬퍼가 깔끔하다.
std::vector<int> v = {1, 2, 3, 4, 5};
absl::Span<int> s1 = absl::MakeSpan(v); // 전체absl::Span<int> s2 = absl::MakeSpan(v.data(), 3); // 처음 3개absl::Span<int> s3 = absl::MakeSpan(&v[1], 2); // {2, 3}absl::Span<int> s4(v); // 직접 생성
int arr[10];absl::Span<int> s5(arr); // sizeof 추론 → size 10const T 데이터를 받으려면 Span<const T>.
void Read(absl::Span<const int> s); // 읽기 전용void Write(absl::Span<int> s); // 수정 가능#슬라이싱
absl::Span<int> s = absl::MakeSpan(v);
auto first3 = s.subspan(0, 3);auto last2 = s.last(2);auto skip1 = s.subspan(1); // 1번부터 끝까지
s.front(); // s[0]s.back(); // s[size-1]s.size(); // 원소 개수s.empty();s.data(); // 첫 원소 주소#컨테이너 인터페이스 호환
absl::Span은 begin()/end()를 제공하므로 range-based for·STL 알고리즘에 그대로 들어간다.
void PrintAll(absl::Span<const int> s) { for (int x : s) std::cout << x << " ";}
int Sum(absl::Span<const int> s) { return std::accumulate(s.begin(), s.end(), 0);}#함수 매개변수 가이드
| 입력 의도 | 시그니처 |
|---|---|
| 읽기 전용, 어떤 contiguous든 | absl::Span<const T> |
| 쓰기 가능, 어떤 contiguous든 | absl::Span<T> |
| 소유권 이양 | std::vector<T> (값) |
| 컨테이너 타입 강제 | 구체 타입 (std::vector<T>, std::array<T, N>) |
| 단일 원소 | const T& |
대부분의 읽기 인터페이스는 Span<const T>로 통일하면 호출 측 boilerplate가 사라진다.
#함정 — dangling
Span은 view 이므로 원본보다 오래 살면 dangle.
// 회피 — 임시 객체의 데이터absl::Span<int> Bad() { std::vector<int> v = {1, 2, 3}; return absl::MakeSpan(v); // ❌ v 소멸 → dangling}
// 회피 — initializer_list 임시void Use() { absl::Span<const int> s = {1, 2, 3}; // ❌ {1,2,3}은 lifetime이 statement 끝 — 다음 줄에서 dangling Process(s);}
// Good — 같은 statement 안에서만 사용Process(absl::Span<const int>{1, 2, 3});string_view와 같은 dangling 위험을 가진다(다음 시리즈의 anti-pattern 참조).
#다차원 — Span<Span> 안 됨
absl::Span은 연속 메모리 가정이다. 행이 흩어진 2D를 직접 표현하지 않는다.
// 회피absl::Span<absl::Span<int>> matrix; // ❌ Span 배열은 연속 메모리 아님 (대부분)
// Good — flat + stridestruct Matrix { absl::Span<float> data; int rows, cols; float& At(int r, int c) { return data[r * cols + c]; }};다차원이 필요하면 mdspan(C++23) 또는 직접 stride 관리.
#작은 예시 — 청크 처리
void ProcessInChunks(absl::Span<const uint8_t> input, size_t chunk_size) { for (size_t off = 0; off < input.size(); off += chunk_size) { size_t n = std::min(chunk_size, input.size() - off); absl::Span<const uint8_t> chunk = input.subspan(off, n); ProcessChunk(chunk); }}
// 호출 측std::vector<uint8_t> buf = ReadFile();ProcessInChunks(absl::MakeSpan(buf), 4096);원본의 어떤 부분이든 추가 복사 없이 전달.
#회피 패턴
// 회피 — vector를 const& 받으면 호출자가 vector 만들어야 함void Sum(const std::vector<int>& v);int arr[] = {1, 2, 3};Sum(std::vector<int>(std::begin(arr), std::end(arr))); // ❌ 복사
// Goodvoid Sum(absl::Span<const int> s);Sum(arr); // 복사 없음// 회피 — Span으로 받았는데 size_t* 추가void Read(absl::Span<const int> s, size_t* count); // ❌ count는 s.size()
// Goodvoid Read(absl::Span<const int> s) { for (int x : s) { /* ... */ }}#정리
absl::Span은 연속 메모리의 non-owning view.std::span(C++20)의 dynamic-extent 짝. 인터페이스 호환.- 읽기 인터페이스는
Span<const T>로 통일하면 모든 contiguous container 수용. - 슬라이싱:
subspan,first,last. STL 알고리즘 그대로 사용. - dangling 위험은
string_view와 동일 — 원본보다 길게 살 수 없음.
#다음 장 예고
Part 9-06: absl::any — type-erased single-value container.
#관련 항목
- Part 4-01: string_view — 같은 정신의 문자열 view
- Part 4-02: string_view pitfalls — dangling 위험
- Effective Modern C++ — 항목 21: make_unique
- 원문 — absl::Span
Abseil Code Review · 53 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::any 분석
absl::any — 임의 타입을 담는 type-erased 컨테이너. variant와 언제 어떻게 다른가.
같은 시리즈에서 이어 읽기
absl::variant 분석
absl::variant — std::variant의 polyfill. 타입-안전 union과 visitor 패턴.
같은 시리즈에서 이어 읽기
absl::optional vs std::optional
absl::optional — std::optional이 도착하기 전 시기의 polyfill. 지금은 std::optional의 alias로 동작.
같은 시리즈에서 이어 읽기