47. 멤버 함수의 참조 한정자(ref-qualifier)와 값 범주 기반 오버로드
난이도 중내 리뷰 · C++
해설 · C++
해설 — 멤버 함수의 참조 한정자(ref-qualifier)와 값 범주 기반 오버로드
난이도: 중상
요약
멤버 함수 뒤의 &/&&/const & 는 호출 대상 객체(*this)의 값 범주로 오버로드를 고르게 한다.
lvalue 객체에서는 const & 버전이, rvalue(임시·std::move 된) 객체에서는 && 버전이 선택된다.
그래서 임시 객체에서는 안전하게 내부 자원을 이동해 복사를 피할 수 있다.
출력
get() const& -> copy
get() && -> move
get() && -> move
lvalue / temp / lvalue
핵심 개념
- ref-qualifier 는
*this의 값 범주를 대상으로 한 오버로드:get() const &는 "이 객체가 lvalue 일 때",get() &&는 "이 객체가 rvalue 일 때" 호출된다. 일반 함수의 인자 오버로드 (f(const T&)vsf(T&&))와 같은 원리를, 암시적 인자인*this에 적용한 것이다. - 값 범주 판정:
(1) lv.get():lv는 이름 있는 lvalue →const &선택 → copy.a = "lvalue".(2) makePayload().get(): 함수가 돌려준 prvalue(임시) →&&선택 → move.b = "temp".(3) std::move(lv).get():std::move가lv를 xvalue(rvalue)로 캐스팅 →&&선택 → move.c = "lvalue"(이동으로 가져온 값). 이후lv.data는 moved-from 상태다.
단계별 내부 동작
- 오버로드 집합에 ref-qualifier 가 있으면, 컴파일러는 호출식의 객체 표현식이 lvalue 인지 rvalue 인지 판정한다(값 범주 분석).
- lvalue 면
&(또는const &) 후보만, rvalue 면&&(그리고 rvalue 가 const-lvalue 참조에도 바인딩 가능하므로const &도) 후보가 된다. 그중 가장 잘 맞는 것을 고른다 — 여기선 rvalue 에&&가const &보다 정확히 일치해 우선한다. - 규칙 일관성: 한 멤버 함수 이름에 ref-qualified 버전을 하나라도 두면, 같은 시그니처의 모든 오버로드가 ref-qualified 이어야 한다(ref-qualified 와 비-qualified 를 섞을 수 없다).
&&오버로드 안에서std::move(data)로 멤버를 이동한다. 대상 객체가 곧 소멸할 rvalue 이기 때문에 내부 자원을 훔쳐도 안전하다는 것이 이 오버로드의 존재 이유다.
흔한 오해·함정
- "
std::move(x).f()는 x 를 이동한다" 가 아니라,&&오버로드를 선택할 뿐이다. 실제 이동은 그 오버로드가 내부에서std::move를 할 때 일어난다(안 하면 이동 안 됨). const &&는 거의 무의미: const rvalue 는 이동할 수 없으므로 최적화 이득이 없다.- lvalue-only 강제:
T& operator=(...) &처럼&만 두면 임시 객체에 대입하는 실수 (makePayload() = other;)를 컴파일 단계에서 막을 수 있다(표준 라이브러리도 일부 대입 연산자에 사용). - moved-from 객체는 유효하지만 미지정 상태 — 이후 재사용 전 재대입해야 한다.
면접 포인트
- ref-qualifier 가 인자 오버로드(
const T&/T&&)와 어떻게 대응되는가(*this의 값 범주). - 빌더/게터에서 임시 객체의 자원을 이동해 복사를 없애는 실전 패턴,
std::move(obj).method()의 의미. &한정으로 rvalue 대상 호출(임시에 대입 등)을 컴파일 타임에 금지하는 활용.