Rvalue Ref

필요성

  • C++11부터 등장한 Move SemanticsForwarding을 위해 도입.
  • 상수성 없이 rvalue를 받을 수 있는 문법 필요 → rvalue reference (T&&)

Reference 규칙

  • lvalue reference: lvalue만 바인딩.
  • const lvalue reference: lvalue, rvalue 모두 바인딩 가능.
  • rvalue reference (T&&): rvalue만 바인딩.

예시

  void foo(Point& pt);       // lvalue ref
void foo(Point&& pt);      // rvalue ref

Point pt{1, 2};

Point& r1 = pt;            // lvalue ref
foo(r1);                   // foo(Point&) 호출

Point&& r2 = Point{1, 2};  // rvalue ref
foo(r2);                   // r2는 lvalue, foo(Point&) 호출

foo(static_cast<Point&&>(r2)); // rvalue로 캐스팅 → foo(Point&&)
foo(static_cast<Point&&>(pt)); // lvalue를 rvalue로 캐스팅 → foo(Point&&)
foo(static_cast<Point>(pt));   // 복사본 생성 → foo(Point&&)
  
  • rvalue reference 타입으로 선언해도 변수 이름이 있으면 lvalue.
  • static_cast<T&&>를 통해 rvalue로 변환 가능.

Reference Collapsing

규칙

  • ref to ref는 일반적으로 불가능.
  • 그러나 typedef, decltype, template parameter에서는 ref to ref 가능.
  • Reference collapsing 규칙:
    • T& &T&
    • T& &&T&
    • T&& &T&
    • T&& &&T&&

고려할 상황

  • 아래의 f4(n) 호출 시 n이 lvalue면 T = int&로 추론되어 f4(int&)가 호출. 인자가 lvalue이면 → T는 lvalue reference (int&)가 되기때문. 이는 frowarding ref 지원을 위해

Forwarding Reference

정의

  • T&& 형태의 템플릿 인자lvalue와 rvalue를 모두 받을 수 있는 레퍼런스.
  • 상수성 없이, 복사본 없이 lvalue와 rvalue를 모두 받는 함수를 만들 수 있음.

사례

  template<typename T>
void f4(T&& arg) {}

int main() {
    int n = 1;

    f4<int>(1);     // f4(int&&)
    f4<int&>(n);    // f4(int&)

    f4(1);          // T = int → f4(int&&)
    f4(n);          // T = int& → f4(int&)
}
  

중요성

  • Perfect Forwarding, Move Semantics를 구현하는 핵심.

Forwarding Reference vs Overloading

  • Overloading으로 lvalue ref, rvalue ref 버전을 각각 만들 수 있지만, 코드 중복.
  • Forwarding Reference로 하나의 함수에서 모두 처리 가능.

MISC

const T&&

  • const rvalue referencervalue만 전달 가능 + 상수성 부여.
  • 하지만 사용 빈도는 거의 없음.

auto&&

  • **auto&&**는 Forwarding Reference와 동일한 역할.
  • C++20 이후, 함수 인자로 auto&& 사용 가능:
  void f1(T&& arg);
void f2(auto&& arg); // 동일
  
  • template 추론 규칙 적용.