콘텐츠로 이동

Rvalue 참조 (Rvalue References)

rvalue 참조는 아래에 나열한 특정한 경우에만 사용하세요.

정의:

rvalue 참조는 임시 객체에만 바인딩할 수 있는 참조의 한 종류입니다. 문법은 전통적인 참조 문법과 비슷합니다. 예를 들어 void f(std::string&& s);는 인수가 std::string에 대한 rvalue 참조인 함수를 선언합니다.

함수 매개변수에서 한정되지 않은 템플릿 인수에 && 토큰이 적용되면 특별한 템플릿 인수 추론 규칙이 적용됩니다. 그런 참조를 전달 참조(forwarding reference)라고 부릅니다.

장점:

  • 이동 생성자(클래스 타입에 대한 rvalue 참조를 받는 생성자)를 정의하면 값을 복사하는 대신 이동할 수 있습니다. 예를 들어 v1이 std::vector<std::string>이라면 auto v2(std::move(v1))은 많은 양의 데이터를 복사하는 대신 아마도 간단한 포인터 조작만 하게 될 것입니다. 많은 경우 이것이 큰 성능 향상으로 이어집니다.
  • rvalue 참조가 있으면 이동은 되지만 복사는 되지 않는 타입을 구현할 수 있습니다. 복사에 대한 합리적인 정의가 없으면서도 함수 인수로 넘기거나 컨테이너에 담고 싶은 타입에 유용합니다.
  • std::unique_ptr 같은 일부 표준 라이브러리 타입을 제대로 활용하려면 std::move가 필요합니다.
  • rvalue 참조 토큰을 사용하는 전달 참조가 있으면, 자신의 인수를 다른 함수로 넘겨 주는 범용 함수 래퍼를 작성할 수 있고, 그 인수가 임시 객체인지 아닌지나 const인지 아닌지에 관계없이 동작하게 만들 수 있습니다. 이를 '완벽한 전달(perfect forwarding)'이라고 합니다.

단점:

  • rvalue 참조는 널리 이해되어 있지 않습니다. 참조 축약(reference collapsing)이나 전달 참조에 적용되는 특별한 추론 규칙 같은 것들은 다소 알기 어렵습니다.
  • rvalue 참조는 잘못 쓰이는 경우가 많습니다. 함수 호출 뒤에도 인수가 유효하고 명시된 상태를 유지하리라 기대되는 시그니처나, 이동 연산이 전혀 일어나지 않는 시그니처에서 rvalue 참조를 쓰는 것은 직관에 어긋납니다.

결정:

rvalue 참조를 사용하지 마세요(메서드에 && 한정자를 붙이는 것도 마찬가지입니다). 다만 아래 경우는 예외입니다.

  • 이동 생성자와 이동 대입 연산자를 정의하는 데 사용할 수 있습니다(복사 가능 및 이동 가능 타입에서 설명한 대로입니다).
  • *this를 논리적으로 "소비"해서 사용할 수 없거나 비어 있는 상태로 남기는 && 한정 메서드를 정의하는 데 사용할 수 있습니다. 이것은 (함수 시그니처의 닫는 괄호 뒤에 오는) 메서드 한정자에만 해당한다는 점에 유의하세요. 일반 함수 매개변수를 "소비"하고 싶다면 값으로 전달하는 편이 낫습니다.
  • 완벽한 전달을 지원하기 위해 std::forward와 함께 전달 참조를 사용할 수 있습니다.
  • 하나는 Foo&&를 받고 다른 하나는 const Foo&를 받는 식의 오버로드 쌍을 정의하는 데 사용할 수 있습니다. 보통은 그냥 값으로 전달하는 것이 선호되는 해법이지만, 예를 들어 그 함수가 입력을 소비하지 않는 경우가 있다면 오버로드 쌍이 더 나은 성능을 내기도 합니다. 늘 그렇듯이, 성능을 위해 더 복잡한 코드를 작성한다면 그것이 실제로 도움이 된다는 증거를 확보하세요.

옮긴이 풀이

핵심: 정해진 몇 가지 경우에만 사용

rvalue 참조(T&&)는 임시 객체에 바인딩되는 참조입니다. 널리 이해되지 않고 오용되기 쉬우므로, 아래의 정해진 경우에만 쓰세요.

허용되는 경우

  1. 이동 생성자·이동 대입 연산자 정의 (복사 대신 이동으로 성능 향상)
  2. && 한정 메서드: *this를 논리적으로 "소비"하는 메서드 (함수 시그니처 닫는 괄호 뒤 한정자)
  3. 완벽한 전달(perfect forwarding): 전달 참조 + std::forward
  4. 오버로드 쌍(Foo&& / const Foo&): 보통은 값 전달이 낫지만, 성능 이점이 입증될 때
auto v2(std::move(v1));  // 대량 복사 대신 포인터 몇 개만 옮김

주의

전달 참조의 추론 규칙(참조 축약 등)은 알기 어렵고 직관적이지 않습니다. 성능을 위해 복잡한 코드를 쓸 때는 실제로 이득이 있다는 증거를 확인하세요.