프로그래밍 언어/C++

C ++ 11에서 T && (&&)가 의미하는 것

Rateye 2021. 8. 4. 10:54
728x90
반응형
질문 : C ++ 11에서 T && (이중 앰퍼샌드)는 무엇을 의미합니까?

T&& var 와 같은 변수를 선언 할 때 이중 앰퍼샌드를 발견했습니다.

우선이 짐승은 무엇이라고 불릴까요? Google에서 이와 같은 구두점 검색을 허용하기를 바랍니다.

정확히 무엇을 의미합니까?

언뜻보기에는 이중 참조 (예 : C 스타일 이중 포인터 T** var )로 보이지만 사용 사례를 생각하는 데 어려움을 겪고 있습니다.

답변

rvalue 참조 (표준 제안 문서)를 선언합니다.

다음은 rvalue 참조에 대한 소개입니다.

다음은 Microsoft의 표준 라이브러리 개발자 중 한 명이 작성한 rvalue 참조에 대한 환상적인 심층 분석입니다.

주의 : MSDN의 링크 된 문서 ( "Rvalue 참조 : VC10의 C ++ 0x 기능, 파트 2")는 Rvalue 참조에 대한 매우 명확한 소개이지만 C ++ 11 초안에서 한때 참이었던 Rvalue 참조에 대한 설명을 제공합니다. 표준이지만 마지막 것은 사실이 아닙니다! 특히, 여러 지점에서 rvalue 참조가 한때 참 이었지만 변경된 lvalue에 바인딩 할 수 있다고 말합니다. (예 : int x; int && rrx = x; 더 이상 GCC에서 컴파일되지 않음) – drewbarbs

C ++ 03 참조 (현재 C ++ 11에서는 lvalue 참조라고 함) 간의 가장 큰 차이점은 const가 아니어도 임시처럼 rvalue에 바인딩 할 수 있다는 것입니다. 따라서이 구문은 이제 합법적입니다.

T&& r = T();

rvalue 참조는 주로 다음을 제공합니다.

의미를 이동합니다 . 이제 일반적인 const-lvalue 참조 대신 rvalue 참조를 사용하는 이동 생성자와 이동 할당 연산자를 정의 할 수 있습니다. 이동은 원본을 변경하지 않고 유지할 의무가 없다는 점을 제외하고는 사본과 같은 기능을합니다. 실제로 일반적으로 이동 된 리소스를 더 이상 소유하지 않도록 소스를 수정합니다. 이는 특히 표준 라이브러리 구현에서 불필요한 복사본을 제거하는 데 유용합니다.

예를 들어 복사 생성자는 다음과 같습니다.

foo(foo const& other)
{
    this->length = other.length;
    this->ptr = new int[other.length];
    copy(other.ptr, other.ptr + other.length, this->ptr);
}

만약이 생성자가 임시로 전달 되었다면, 우리는 임시가 파괴 될 것이라는 것을 알고 있기 때문에 사본이 불필요 할 것입니다. 이미 할당 된 임시 자원을 사용하지 않는 이유는 무엇입니까? C ++ 03에서는 임시로 전달되었는지 여부를 확인할 수 없기 때문에 복사를 방지 할 방법이 없습니다. C ++ 11에서는 이동 생성자를 오버로드 할 수 있습니다.

foo(foo&& other)
{
   this->length = other.length;
   this->ptr = other.ptr;
   other.length = 0;
   other.ptr = nullptr;
}

여기서 큰 차이점을 확인하십시오. 이동 생성자는 실제로 인수를 수정합니다. 이렇게하면 임시를 구성중인 개체로 효과적으로 "이동"하여 불필요한 복사본을 제거합니다.

std::move 함수를 사용하여 rvalue 참조로 명시 적으로 변환되는 임시 및 비 상수 lvalue 참조에 사용됩니다 (변환 만 수행함). f1f2 대한 이동 생성자를 호출합니다.

foo f1((foo())); // Move a temporary into f1; temporary becomes "empty"
foo f2 = std::move(f1); // Move f1 into f2; f1 is now "empty

완벽한 전달 . rvalue 참조를 사용하면 템플릿 함수에 대한 인수를 적절하게 전달할 수 있습니다. 예를 들어 다음 공장 기능을 사용하십시오.

template <typename T, typename A1>
std::unique_ptr<T> factory(A1& a1)
{
    return std::unique_ptr<T>(new T(a1));
}

factory<foo>(5) 호출하면 인수는 int& foo 의 생성자가 int 취하더라도 리터럴 5에 바인딩되지 않습니다. 글쎄, 우리는 대신 A1 const& 를 사용할 수 있지만 foo 가 비 상수 참조로 생성자 인수를 취하면 어떻게 될까요? 진정으로 일반적인 팩토리 함수를 만들려면 A1&A1 const& 에 팩토리를 오버로드해야합니다. 팩토리가 1 개의 매개 변수 유형을 취하면 괜찮을 수 있지만 각 추가 매개 변수 유형은 필요한 과부하 세트에 2를 곱합니다. 이는 매우 빠르게 유지 보수 할 수 없습니다.

rvalue 참조는 표준 라이브러리가 lvalue / rvalue 참조를 적절하게 전달할 수 std::forward 함수를 정의하도록 허용함으로써이 문제를 해결합니다. std::forward 작동 방식에 대한 자세한 내용 은이 훌륭한 답변을 참조하십시오.

이를 통해 다음과 같이 팩토리 기능을 정의 할 수 있습니다.

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

이제 인수의 rvalue / lvalue-ness는 T 의 생성자에 전달 될 때 보존됩니다. 즉, factory가 rvalue로 호출되면 T 의 생성자가 rvalue로 호출됩니다. 팩토리가 lvalue로 호출되면 T 의 생성자가 lvalue로 호출됩니다. 개선 된 공장 기능은 다음과 같은 특별한 규칙 때문에 작동합니다.

함수 매개 변수 유형이 T&& 형식이고 여기서 T 는 템플릿 매개 변수이고 함수 인수가 A 유형의 lvalue이면 유형 A& 가 템플릿 인수 추론에 사용됩니다.

따라서 공장을 다음과 같이 사용할 수 있습니다.

auto p1 = factory<foo>(foo()); // calls foo(foo&&)
auto p2 = factory<foo>(*p1);   // calls foo(foo const&)

중요한 rvalue 참조 속성 :

  • 과부하 해결을 위해 lvalues는 lvalue 참조에 대한 바인딩을 선호하고 rvalue는 rvalue 참조에 대한 바인딩을 선호합니다 . 따라서 임시 사용자가 복사 생성자 / 할당 연산자보다 이동 생성자 / 이동 할당 연산자를 호출하는 것을 선호하는 이유입니다.
  • rvalue 참조는 암시 적으로 rvalue와 암시 적 변환의 결과 인 임시에 바인딩됩니다 . 즉, float f = 0f; int&& i = f; float는 암시 적으로 int로 변환 할 수 있기 때문에 잘 형성됩니다. 참조는 변환의 결과 인 임시에 대한 것입니다.
  • 명명 된 rvalue 참조는 lvalue입니다. 명명되지 않은 rvalue 참조는 rvalue입니다. std::move 호출이 필요한 이유를 이해하는 데 중요합니다 foo&& r = foo(); foo f = std::move(r);
출처 : https://stackoverflow.com/questions/5481539/what-does-t-double-ampersand-mean-in-c11
728x90
반응형