33. 구조화된 바인딩(structured bindings)의 내부 동작
난이도 중내 리뷰 · C++
해설 · C++
해설 — 구조화된 바인딩(structured bindings)의 내부 동작
난이도: 중
요약
출력은 A p.x=99 p.y=20, B p.x=99 cx=-1, C arr={1,200,3}, D ti=7 td=3.5. auto [a,b] = e 는 이름 a,b를 만드는 게 아니라, **숨은 객체 e를 하나 만들고 a,b는 그 하위 객체(멤버/배열원소/get 결과)의 별명(alias)**이다. auto&면 숨은 객체가 원본을 참조하므로 바인딩 수정이 원본에 반영되고, auto(값)면 숨은 객체가 복사본이라 원본과 무관하다.
핵심 개념
구조화된 바인딩은 컴파일러가 대략 다음처럼 확장한다:
auto& [rx, ry] = p;
// ↓ 개념적 확장
auto& __e = p; // 숨은 이름 __e (여기선 참조)
// rx 는 __e.x 의 별명, ry 는 __e.y 의 별명
rx,ry는 참조 타입이 아니다(타입 질의 시int). 하지만 언어가 "이 이름은__e의 특정 하위 객체를 가리킨다"고 규정해, 접근하면 항상__e의 그 하위 객체를 읽고 쓴다. 그래서 참조처럼 동작한다.
단계별 내부 동작
- (A)
auto& [rx,ry]=p:__e가p를 참조.rx = 99는p.x를 바꾼다 →p.x=99, p.y=20. - (B)
auto [cx,cy]=p:__e는p의 복사본.cx=-1은 복사본만 바꾼다 → 원본p.x는 (A)에서 바뀐 99 그대로,cx=-1. - (C) 배열
auto& [a0,a1,a2]=arr: 배열도 각 원소의 별명으로 분해된다(get이 아니라 원소 직접).a1=200은arr[1]을 바꿈 →{1,200,3}. - (D) tuple
auto [ti,td]=t: tuple 은std::tuple_size/std::tuple_element/get<i>프로토콜로 분해된다. 값 복사라ti=7, td=3.5(원본 무관).
세 가지 분해 경로
컴파일러는 대상 타입에 따라 다른 규칙을 쓴다:
- 배열: 각 원소가 하나의 바인딩.
get불필요. - tuple-like:
std::tuple_size<E>::value로 개수를 알고, 각 바인딩은get<i>(e)(멤버 또는 비멤버) 결과의 별명.std::tuple,std::pair,std::array및 사용자가 프로토콜을 구현한 타입. - 공개 데이터 멤버(구조체): 비정적 공개 멤버가 순서대로 각 바인딩.
Point{x,y}가 이 경로.
흔한 오해·함정
- "바인딩 이름은 참조다" — 아니다.
decltype(rx)는 참조가 아니라 하위 객체의 타입(값 카테고리·cv 는 별개 규칙). 다만 동작은 별명이라 원본을 바꾼다(auto& 일 때). auto값 바인딩도 복사가 한 번 일어난다 — 큰 객체를 값으로 분해하면 전체 복사 비용. 읽기만 하면const auto&권장.- tuple-like 는
get<i>가 참조를 돌려주므로auto&면 원본 tuple 원소를 별명.auto면 개별 원소가 아니라 tuple 전체 복사본에서 별명. - 구조화된 바인딩은 람다 캡처(C++17)나 constexpr 문맥에서 제약이 있었다(이후 완화). 캡처하려면 명시 캡처 주의.
면접 포인트
auto [a,b]의 "숨은 객체 + 별명" 모델을 설명하고,autovsauto&vsconst auto&의 복사/참조/원본 반영 차이를 정확히 말할 수 있는가.- 세 가지 분해 경로(배열 / tuple-like
get<i>/ 공개 멤버)와 각각의 트리거 조건. - 큰 객체를 값으로 분해할 때의 복사 비용, 그리고
decltype(바인딩)이 참조가 아니라는 미묘함.