다음을 통해 공유


Rvalue 참조 선언 자: & &

rvalue 식에 대한 참조를 보유합니다.

type-id && cast-expression

설명

Rvalue 참조를 사용하면 lvalue와 rvalue를 구별할 수 있습니다.Lvalue 참조와 rvalue 참조는 구문 및 의미 체계가 비슷하지만 다소 다른 규칙을 따라야 합니다.lvalue 및 rvalue에 대한 자세한 내용은 Lvalue가 고 Rvalue를 참조하십시오.lvalue 참조에 대한 자세한 내용은 Lvalue 참조 선언 자: &를 참조하십시오.

다음 단원에서는 rvalue 참조가 의미 체계 이동 및 완벽한 전달의 구현을 지원하는 방법을 설명합니다.

의미 체계 이동

Rvalue 참조는 의미 체계 이동의 구현을 지원해 응용 프로그램의 성능을 상당히 높일 수 있습니다.의미 체계 이동을 사용하여 한 개체에서 다른 개체로 리소스 (예: 동적으로 할당된 메모리)를 전송하는 코드를 작성할 수 있습니다.의미 체계 이동은 리소스가 프로그램의 다른 곳에서 참조될 수 없는 임시 개체에서 전송될 수 있도록 하기 때문에 작동합니다.

이동 문법을 실행시키려면, 보통 move constructor, 와 선택적으로 이동 할당 연산자 (operator=)를 클래스에 제공해야 합니다.그런 다음 해당 소스가 rvalues인 복사 및 할당 작업은 의미 체계 이동을 자동으로 사용합니다.기본 복사 생성자와는 달리 컴파일러는 이동하는 기본 생성자를 제공하지 않습니다.이동 생성자를 작성하는 방법과 이 생성자를 응용 프로그램에서 사용하는 방법에 대한 자세한 내용은 방법: 이동 생성자 작성를 참조하십시오.

일반 함수 및 연산자를 오버로드하여 의미를 이동합니다.Visual C++ 2010은 STL(표준 템플릿 라이브러리)에 의미 체계 이동을 제공합니다.예를 들어, string 클래스는 의미 체계 이동을 수행하는 작업을 구현합니다.여러 문자열을 연결하고 결과를 출력하는 다음 예제를 살펴보십시오.

// string_concatenation.cpp
// compile with: /EHsc
#include <iostream>
#include <string>
using namespace std;

int main()
{
   string s = string("h") + "e" + "ll" + "o";
   cout << s << endl;
}

Visual C++ 2010 전에 operator+를 호출할 때마다 새 임시 string 개체(rvalue)가 할당되고 반환됩니다.operator+는 소스 문자열 lvalue 또는 rvalue가 있는지 알 수 없으므로 하나의 문자열에 추가할 수 없습니다.소스 문자열이 모두 lvalue인 경우 프로그램의 다른 곳에서 참조될 수 있으므로 수정하지 않아야 합니다.rvalue 참조를 사용함으로써 operator+는 프로그램 어디서나 참조할 수 없는 rvalues를 취하도록 수정할 수 있습니다.따라서 operator+가 지금 한 문자열을 다른 문자열에 추가할 수 있습니다.이것은 string 클래스가 수행해야 하는 동적 메모리 할당 수를 상당히 줄일 수 있습니다.string 클래스에 대한 자세한 내용은 basic_string Class을 참조하십시오.

의미 체계 이동은 컴파일러에서 RVO(반환 값 최적화) 또는 NRVO(명명된 반환 값 최적화)를 사용할 수 없는 경우에도 도움이 됩니다.이러한 경우 컴파일러는 형식을 정의하는 경우 이동 생성자를 호출합니다.명명된 반환 값 최적화에 대한 자세한 내용은 Named Return Value Optimization in Visual C++ 2005를 참조하십시오.

의미 체계 이동에 대한 자세한 내용은 요소를 vector 개체에 삽입하는 예를 참조하십시오.vector 개체의 용량이 초과될 경우 vector 개체는 해당 요소를 위해 메모리를 재할당한 다음 각 요소를 다른 메모리 위치로 복사하여 삽입된 메모리를 위한 공간을 만들어야 합니다.삽입 작업이 요소를 복사할 때 새 요소를 만들고, 데이터를 이전 요소에서 새 요소로 복사하는 복사 생성자를 호출한 후 이전 요소를 소멸 시킵니다.의미 체계 이동을 사용하여 비용이 많이 드는 메모리 할당과 복사 작업을 수행할 필요 없이 개체를 직접 이동할 수 있습니다.

vector예시에서 이동 의미론을 활용하기 위해, 하나의 개체에서 다른 개체로 데이터를 이동하는 이동 생성자를 작성할 수 있습니다.

Visual C++ 2010에서 STL로의 의미 체계 이동 소개에 대한 자세한 내용은 표준 C++ 라이브러리 참조를 참조하십시오.

완벽하게 전달

완벽한 전달은 오버 로드된 함수의 필요성을 줄이고 전달 문제를 방지하는데 도움이 됩니다.전달 문제는 매개 변수로 참조를 사용하는 제네릭 함수를 작성하여 다른 함수에 이 매개변수 (또는 전달)를 전달합니다.예를 들어 제네릭 함수는 const T& 형식의 매개 변수를 사용하는 경우, 호출된 함수는 해당 매개 변수의 값을 수정할 수 없습니다.제네릭 함수에서 T& 형식의 매개 변수를 사용하는 경우, rvalue(예: 임시 개체 또는 정수 리터럴)을 사용하여 이 함수가 호출될 수 없습니다.

일반적으로 이 문제를 해결하려면 각 매개 변수에 대해 T& 및 const T&를 모두 사용하는 제네릭 함수의 오버로드된 버전을 제공해야 합니다.따라서 오버로드된 함수의 수가 매개 변수의 수와 함께 엄청나게 증가합니다.Rvalue 참조를 사용하면 임의의 인수를 허용하여 다른 함수가 직접 호출된 것처럼 다른 함수로 전달하는 함수의 버전을 하나 작성할 수 있습니다.

W, X, Y 및 Z의 4가지 형식을 선언하는 다음 예제를 살펴보십시오.각 형식에 대한 생성자는 const 및 비 const lvalue 참조의 다른 조합을 매개 변수로 사용합니다.

struct W
{
   W(int&, int&) {}
};

struct X
{
   X(const int&, int&) {}
};

struct Y
{
   Y(int&, const int&) {}
};

struct Z
{
   Z(const int&, const int&) {}
};

개체를 생성하는 제네릭 함수를 작성한다고 가정합니다.다음 예제에서는 이 함수를 작성하는 한 가지 방법을 보여 줍니다.

template <typename T, typename A1, typename A2>
T* factory(A1& a1, A2& a2)
{
   return new T(a1, a2);
}

다음 예제에서는 factory 함수에 대한 유효한 호출을 보여 줍니다.

int a = 4, b = 5;
W* pw = factory<W>(a, b);

그러나 다음 예제에서는 factory가 rvalue를 사용하여 lvalue 참조를 매개 변수로 수정이 가능하기 때문에 factory 함수가 올바른 호출을 포함하지 않습니다.

Z* pz = factory<Z>(2, 2);

일반적으로 이 문제를 해결하려면 A& 및 const A& 매개 변수의 모든 조합에 대해 factory 함수의 오버로드된 버전을 만들어야 합니다.Rvalue 참조를 사용하면 다음 예제와 같이 factory 함수의 버전을 하나 쓸 수 있습니다.

template <typename T, typename A1, typename A2>
T* factory(A1&& a1, A2&& a2)
{
   return new T(std::forward<A1>(a1), std::forward<A2>(a2));
}

이 예제에서는 rvalue 참조를 factory 함수에 대한 매개 변수로 사용합니다.std::forward 함수는 템플릿 클래스의 생성자에 공장 함수 매개 변수를 전달합니다.

다음 예제는 W, X, Y, Z클래스의 인스턴스를 만들기 위해 수정된 factory함수를 사용하는 main함수를 보여줍니다.개정된 factory 함수는 해당 매개 변수(lvalue 또는 rvalue)를 적절한 클래스 생성자에 전달합니다.

int main()
{
   int a = 4, b = 5;
   W* pw = factory<W>(a, b);
   X* px = factory<X>(2, b);
   Y* py = factory<Y>(a, 2);
   Z* pz = factory<Z>(2, 2);

   delete pw;
   delete px;
   delete py;
   delete pz;
}

Rvalue 참조의 추가 속성

Lvalue에 대한 참조 및 rvalue 참조를 사용하는 함수를 오버로드할 수 있습니다.

const lvalue 참조 또는 rvalue 참조를 취하도록 함수를 오버로드함으로써 수정할 수 없는 개체(lvalues)와 수정할 수 있는 임시 값(rvalues)을 구분하는 코드를 작성할 수 있습니다.개체가 const로 마크되지 않으면 개체를 rvalue 참조를 취하는 함수로 전달할 수 있습니다.다음 예제에서는 lvalue 참조 및 rvalue 참조를 사용하도록 오버로드되는 f 함수를 보여 줍니다.main 함수는 lvalue와 rvalue를 사용하여 f를 호출합니다.

// reference-overload.cpp
// Compile with: /EHsc
#include <iostream>
using namespace std;

// A class that contains a memory resource.
class MemoryBlock
{
   // TODO: Add resources for the class here.
};

void f(const MemoryBlock&)
{
   cout << "In f(const MemoryBlock&). This version cannot modify the parameter." << endl;
}

void f(MemoryBlock&&)
{
   cout << "In f(MemoryBlock&&). This version can modify the parameter." << endl;
}

int main()
{
   MemoryBlock block;
   f(block);
   f(MemoryBlock());
}

이 예제의 결과는 다음과 같습니다.

In f(const MemoryBlock&). This version cannot modify the parameter.
In f(MemoryBlock&&). This version can modify the parameter.

이 예제에서 f 에 대한 첫 번째 호출은 로컬 변수(lvalue)를 인수로 전달합니다.두 번째 f 호출에서 임시 개체를 인수로 전달합니다.임시 개체는 프로그램 어디서나 참조할 수 없기 때문에 호출은 개체를 언제든지 수정할 수 있는 rvalue 참조를 이용하는 f의 오버로드된 버전에 바인딩합니다.

컴파일러는 명명된 rvalue 참조를 lvalue로 취급하고 명명되지 않은 rvalue 참조를 rvalue로 취급합니다.

Rvalue 참조를 매개 변수로 사용하는 함수를 작성하는 경우, 해당 매개 변수는 함수 본문에 lvalue로 처리됩니다.컴파일러는 명명된 개체가 프로그램의 여러 부분에서 참조될 수 있기 때문에 명명된 rvalue 참조를 lvalue로 취급합니다. 프로그램의 여러 부분에서 해당 개체의 리소스를 수정하거나 제거할 수 있도록 허용하는 것은 위험할 수 있습니다.예를 들어, 프로그램의 여러 부분이 동일한 개체에서 리소스를 전송하려고 하면 첫 번째 부분만 리소스를 성공적으로 전송합니다.

다음 예제에서는 lvalue 참조 및 rvalue 참조를 사용하도록 오버로드되는 g 함수를 보여 줍니다.함수 f는 매개 변수로서 rvalue 참조(명명된 rvalue 참조)를 사용하고 rvalue 참조(이름 없는 rvalue 참조)을 반환합니다.f에서 g를 호출할 때 f 본문에서 매개 변수를 lvalue로 처리하기 때문에 오버로드 확인은 lvalue 참조를 사용하는 g의 버전을 선택합니다.main에서 g를 호출할 때 f에서 rvalue 참조를 반환하기 때문에 오버로드 확인은 rvalue 참조를 사용하는 g의 버전을 선택합니다.

// named-reference.cpp
// Compile with: /EHsc
#include <iostream>
using namespace std;

// A class that contains a memory resource.
class MemoryBlock
{
   // TODO: Add resources for the class here.
};

void g(const MemoryBlock&) 
{
   cout << "In g(const MemoryBlock&)." << endl;
}

void g(MemoryBlock&&) 
{
   cout << "In g(MemoryBlock&&)." << endl;
}

MemoryBlock&& f(MemoryBlock&& block)
{
   g(block);
   return block;
}

int main()
{
   g(f(MemoryBlock()));
}

이 예제의 결과는 다음과 같습니다.

In g(const MemoryBlock&).
In g(MemoryBlock&&).

이 예제에서 main 함수는 f로 값을 전달합니다. f 의 본문은 lvalue로 명명된 매개 변수를 처리합니다.f에서 g로의 호출은 매개 변수를 lvalue 참조(g의 첫 번째 오버로드된 버전)에 바인딩합니다.

  • Lvalue에서 rvalue 참조를 캐스팅할 수 있습니다.

STL std::move 함수를 사용하여 rvalue 참조 개체를 해당 개체로 변환할 수 있습니다.또는 static_cast 키워드를 사용하여 다음 예제에 표시된 대로 lvalue를 rvalue 참조로 캐스팅할 수 있습니다.

// cast-reference.cpp
// Compile with: /EHsc
#include <iostream>
using namespace std;

// A class that contains a memory resource.
class MemoryBlock
{
   // TODO: Add resources for the class here.
};

void g(const MemoryBlock&) 
{
   cout << "In g(const MemoryBlock&)." << endl;
}

void g(MemoryBlock&&) 
{
   cout << "In g(MemoryBlock&&)." << endl;
}

int main()
{
   MemoryBlock block;
   g(block);
   g(static_cast<MemoryBlock&&>(block));
}

이 예제의 결과는 다음과 같습니다.

In g(const MemoryBlock&).
In g(MemoryBlock&&).

 

함수 템플릿은 해당 템플릿 인수 형식을 추론한 다음 참조 축소 규칙을 사용합니다.

일반적으로 매개 변수를 다른 함수에 전달(전달) 하는 함수 템플릿을 습니다.템플릿 형식 추론이 rvalue 참조를 사용하는 함수 템플릿에 대해 어떻게 작용하는가를 이해하는 것이 중요합니다.

함수 인수가 rvalue이면 컴파일러는 인수를 rvalue 참조로 추론합니다.예를 들어 T&& 형식을 매개 변수로 사용하는 템플릿 함수에 X 형식의 개체에 대한 rvalue 참조를 전달하는 경우, 템플릿 인수 추론에서는 T가 X인 것으로 추론합니다.따라서 매개 변수의 형식은 X&& 입니다.함수 인수가 lvalue 또는 const 값인 경우 컴파일러는 해당 형식을 lvalue 참조 또는 해당 형식의 const lvalue 참조로 추론합니다.

다음 예제에서는 다양한 참조 형식에 대해 하나의 구조체 템플릿을 선언한 다음 특수화합니다.print_type_and_value 함수는 매개 변수로 rvalue 참조를 사용하고, 이를 S::print 메서드의 적절히 특수화된 버전으로 전달합니다.main 함수에서는 S::print 메서드를 호출하는 다양한 방법을 보여 줍니다.

// template-type-deduction.cpp
// Compile with: /EHsc
#include <iostream>
#include <string>
using namespace std;

template<typename T> struct S;

// The following structures specialize S by 
// lvalue reference (T&), const lvalue reference (const T&), 
// rvalue reference (T&&), and const rvalue reference (const T&&).
// Each structure provides a print method that prints the type of 
// the structure and its parameter.

template<typename T> struct S<T&> {
   static void print(T& t)
   {
      cout << "print<T&>: " << t << endl;
   }
};

template<typename T> struct S<const T&> {
   static void print(const T& t)
   {
      cout << "print<const T&>: " << t << endl;
   }
};

template<typename T> struct S<T&&> {
   static void print(T&& t)
   {
      cout << "print<T&&>: " << t << endl;
   }
};

template<typename T> struct S<const T&&> {
   static void print(const T&& t)
   {
      cout << "print<const T&&>: " << t << endl;
   }
};

// This function forwards its parameter to a specialized
// version of the S type.
template <typename T> void print_type_and_value(T&& t) 
{
   S<T&&>::print(std::forward<T>(t));
}

// This function returns the constant string "fourth".
const string fourth() { return string("fourth"); }

int main()
{
   // The following call resolves to:
   // print_type_and_value<string&>(string& && t)
   // Which collapses to:
   // print_type_and_value<string&>(string& t)
   string s1("first");
   print_type_and_value(s1); 

   // The following call resolves to:
   // print_type_and_value<const string&>(const string& && t)
   // Which collapses to:
   // print_type_and_value<const string&>(const string& t)
   const string s2("second");
   print_type_and_value(s2);

   // The following call resolves to:
   // print_type_and_value<string&&>(string&& t)
   print_type_and_value(string("third"));

   // The following call resolves to:
   // print_type_and_value<const string&&>(const string&& t)
   print_type_and_value(fourth());
}

이 예제의 결과는 다음과 같습니다.

print<T&>: first
print<const T&>: second
print<T&&>: third
print<const T&&>: fourth

print_type_and_value 함수로 각 호출을 해결하려면 컴파일러는 템플릿 인수 추론을 먼저 수행해야 합니다.컴파일러는 매개 변수 형식을 추론된 템플릿 인수로 대체할 때 참조 축소 규칙을 적용합니다.예를 들어, 지역 변수 s1을 print_type_and_value 함수로 전달하면 컴파일러에서 다음의 함수 시그니처를 생성하게 됩니다.

print_type_and_value<string&>(string& && t)

컴파일러는 참조 축소 규칙을 사용하여 시그니처를 다음과 같이 줄입니다.

print_type_and_value<string&>(string& t)

이 버전의 print_type_and_value 함수는 파라미터를 정확하게 전문화된 버전의 S::print 방법으로 전송합니다.

다음 표는 템플릿 인수 형식 추론에 대한 참조 축소 규칙을 요약합니다.

확장된 형식

축소된 형식

T& &

T&

T& &&

T&

T&& &

T&

T&& &&

T&&

템플릿 인수를 추론은 완벽한 전달을 구현하는 중요한 요소입니다.이 항목의 앞부분에 제공된 완벽한 전달 단원에서는 완벽한 전달에 대해 자세히 설명합니다.

요약

Rvalue 참조는 rvalue와 lvalue를 구별합니다.그들은 여러분이 불필요한 메모리 할당과 사본 연산의 필요성을 제거함으로써 여러분의 애플리케이션의 성능을 향상시키는데 도움을 줄 수 있습니다.그들은 또한 여러분이 임의의 인수들을 수락하여 마치 다른 함수를 직접 요청한 것처럼 이들을 다른 함수로 보내는 함수의 한 버전을 쓸 수 있도록 해 줍니다.

참고 항목

작업

방법: 이동 생성자 작성

참조

단항 연산자 식

Lvalue 참조 선언 자: &

Lvalue가 고 Rvalue

move

forward

기타 리소스

표준 C++ 라이브러리 참조