39. std::optional 의 내부 동작과 값 의미론
난이도 중내 리뷰 · C++
해설 · C++
해설 — std::optional 의 내부 동작과 값 의미론
난이도: 중
요약
std::optional<T> 는 "값이 있을 수도, 없을 수도" 를 힙 할당 없이 표현한다. 내부적으로 T 크기의 정렬된 저장소 + engaged(bool) 플래그를 가지며, 담긴 객체는 그 저장소에 in-place 로 생성/소멸된다. 출력:
-- 1 --
engaged=0
-- 2 --
ctor A
engaged=1
-- 3 --
dtor A
engaged=0
-- 4 --
ctor B
move B
dtor
operator-> 접근
name=B
-- 5 --
fallback()
r=FB
-- end --
dtor B
핵심 개념
- 저장소: 대략
union { char none; T value; }; bool engaged;형태. 힙을 쓰지 않으므로sizeof(optional<T>) >= sizeof(T) + 정렬 패딩. 포인터/힙 간접 참조가 없다. - engaged 플래그: 값의 유무.
has_value()/operator bool이 이를 반환. - 수명 관리:
emplace/생성자가 저장소에 T 를 생성,reset/소멸자가 T 를 소멸. 비어 있으면 T 의 생성자·소멸자는 호출되지 않는다.
단계별 내부 동작
optional<Tracked> o;: 비어 있는 optional.Tracked생성자 호출 없음 → 출력 없음,engaged=0.o.emplace("A"): 저장소에Tracked("A")를 직접 생성(ctor A). 임시 없이 in-place 구성이라 복사/이동이 없다.engaged=1.o.reset(): 담긴Tracked소멸(dtor A),engaged=0.optional<Tracked> p{Tracked("B")}: 먼저 임시Tracked("B")생성(ctor B) → optional 저장소로 이동 생성(move B) → 임시 소멸. 이때 이동당한 임시의name은 비어 있어dtor(빈 이름) 출력.p->name은operator->(전제조건: engaged 이어야 함, 검사 없음)로 "B" 접근.- 빈
q에 대한q ? q->name : fallback():q가 비어operator bool이 false →fallback()평가(fallback()출력, "FB" 반환). 만약q->name을 먼저 평가했다면 전제조건 위반(UB)이었을 것. - 프로그램 종료:
p가 아직 값을 담고 있으므로 스코프 끝에서dtor B.
흔한 오해·함정
- operator / operator-> vs value()*:
*opt/opt->는 전제조건이 engaged 이며 비어 있으면 정의되지 않은 동작(검사 안 함).opt.value()는 비어 있으면std::bad_optional_access예외. 안전이 필요하면value()또는 사전 검사. - value_or 의 인자는 항상 평가된다:
opt.value_or(expensive())는 opt 유무와 무관하게expensive()를 호출한다(값 인자). 무거운 fallback 은 본문처럼opt ? *opt : compute()로 지연 평가해야 한다 — 본 예제의 5번이 그 관용구. - 참조는 못 담는다:
std::optional<T&>는 표준에서 ill-formed. 참조 유무는 포인터(T*)나reference_wrapper로 표현. - moved-from 상태: 4번에서 optional 로 이동한 뒤 임시의
name은 "유효하지만 미지정"(여기선 빈 문자열). optional 자체는 여전히 engaged 상태로 이동될 수 있고,std::move(opt)는 담긴 값만 이동하며 opt 는 여전히 engaged(빈 값이 아님)로 남는다. - 비었을 때 비용 0 아님: 값이 없어도
sizeof(T)만큼 저장소를 점유한다. 큰 T 를 optional 로 많이 두면 메모리 낭비 → 그럴 땐 포인터/unique_ptr고려.
면접 포인트
- optional 이 힙을 쓰지 않고 in-place 저장한다는 점과 그 메모리 레이아웃(union + flag),
sizeof함의. operator*(전제조건, UB) vsvalue()(예외) vsvalue_or(항상 인자 평가)의 차이와 안전한 사용 관용구.- emplace/reset 이 담긴 객체의 생성자·소멸자를 정확히 언제 호출하는지, 임시를 거친 초기화의 이동 발생.