L-value vs R-value

정의

  • L-value: 이름/식별성이 있고 주소를 가질 수 있는 값(변수, 참조 반환값 등)
  • R-value: 주로 임시값/리터럴처럼 곧 사라지는 값
int a = 3;  // a: L-value, 3: R-value

참조 바인딩 규칙 (중요)

파라미터 타입L-value 인수R-value 인수
T&가능불가
const T&가능가능
T&&불가가능
void F1(Knight& k);        // 수정 가능한 lvalue만
void F2(const Knight& k);  // lvalue/rvalue 모두 허용(읽기 중심)
void F3(Knight&& k);       // rvalue 전용
  • F1(Knight())는 컴파일 에러
  • F2(Knight()), F3(Knight())는 가능

오른값 참조 &&

의미

  • T&&는 "이 값은 곧 버릴 가능성이 높은 임시값"을 받는 통로입니다.
  • 이 통로를 통해 자원 복사 대신 소유권 이전(move)을 구현할 수 있습니다.

std::move

  • std::move(x)x를 rvalue로 캐스팅합니다.
  • 이름과 달리 move 자체가 이동을 실행하는 함수는 아닙니다.
  • 실제 이동은 이후 호출되는 move constructor / move assignment에서 일어납니다.
std::string a = "hello";
std::string b = std::move(a); // 여기서 std::string의 move ctor가 호출됨

이동 생성자 vs 이동 대입 연산자

구분시그니처호출 시점
이동 생성자Knight(Knight&& other)새 객체를 만들 때
이동 대입 연산자Knight& operator=(Knight&& other)기존 객체에 대입할 때

실무 팁:

  • move 연산에 noexcept를 붙이면 컨테이너(vector)가 재할당 시 move를 더 적극적으로 선택합니다.

이동 vs 복사

복사

  • 자원을 새로 복제합니다(독립된 두 객체).
  • 원본/대상이 모두 온전합니다.

이동

  • 자원 핸들을 넘기고 원본은 비워 둡니다.
  • 큰 문자열/버퍼/동적 메모리 객체에서 성능 이점이 큽니다.
  • vector 재할당 시 요소를 move하면 복사 비용을 크게 줄일 수 있습니다.

이동 후 객체 상태

  • moved-from 객체는 파괴 가능하고 대입 가능한 유효 상태여야 합니다.
  • 하지만 값 내용은 보장되지 않습니다(보통 "비어 있음"처럼 보일 수 있으나 규약은 아님).
std::string s = "abc";
std::string t = std::move(s);
// s는 유효하지만 값은 미정(unspecified)
s = "reused"; // 재사용은 안전

실전 사용 규칙 + 체크 질문

실전 규칙

  • 소유권 이전 시점에만 std::move를 사용합니다.
  • move 후 원본 값을 곧바로 읽는 습관은 피합니다.
  • const 객체에 std::move를 해도 진짜 move가 안 되는 경우가 많습니다(복사로 귀결).
  • 지역 변수 반환에서 무조건 std::move를 붙이지 않습니다(NRVO/복사 생략 기회 감소 가능).
std::unique_ptr<Knight> MakeKnight() {
    auto k = std::make_unique<Knight>();
    return k; // 보통 자동 이동/복사 생략
}

체크 질문 (스스로 답해보기)

  • std::move가 "이동 실행"이 아니라 "캐스팅"이라는 뜻을 설명할 수 있는가?
  • 왜 moved-from 객체는 "유효하지만 값은 미정"이라고 하는가?
  • const Tstd::move를 걸면 왜 기대한 move가 안 나올 수 있는가?

profile
李家네_공부방

0개의 댓글