std :: reference_wrapper와 간단한 포인터의 차이점은 무엇입니까? 필요가 std::reference_wrapper있습니까? 어디에서

왜 필요가 std::reference_wrapper있습니까? 어디에서 사용해야합니까? 간단한 포인터와 어떻게 다릅니 까? 성능이 간단한 포인터와 어떻게 비교됩니까?



답변

std::reference_wrapper템플릿과 함께 사용하면 유용합니다. 객체에 대한 포인터를 저장하여 객체를 래핑하고 일반적인 의미를 모방하면서 재 할당 및 복사를 허용합니다. 또한 특정 라이브러리 템플릿에 개체 대신 참조를 저장하도록 지시합니다.

펑터를 복사하는 STL의 알고리즘을 고려하십시오. 펑터 자체 대신 펑터를 참조하는 참조 래퍼를 전달하면 해당 복사를 방지 할 수 있습니다.

unsigned arr[10];
std::mt19937 myEngine;
std::generate_n( arr, 10, std::ref(myEngine) ); // Modifies myEngine's state

이것은 작동하기 때문에 …

  • reference_wrappers 오버로드operator() 이므로 참조하는 함수 객체처럼 호출 할 수 있습니다.

    std::ref(myEngine)() // Valid expression, modifies myEngines state
  • … (비) 일반적인 참조와 달리 복사 (및 할당) reference_wrappers는 pointee를 할당합니다.

    int i, j;
    auto r = std::ref(i); // r refers to i
    r = std::ref(j); // Okay; r refers to j
    r = std::cref(j); // Error: Cannot bind reference_wrapper<int> to <const int>

참조 래퍼를 복사하는 것은 포인터를 복사하는 것과 거의 동일합니다. 사용에 내재 된 모든 함수 호출 (예 :에 대한 호출 operator())은 한 줄짜리이므로 인라인되어야합니다.

reference_wrapper들로 만들어집니다 std::refstd::cref :

int i;
auto r = std::ref(i); // r is of type std::reference_wrapper<int>
auto r2 = std::cref(i); // r is of type std::reference_wrapper<const int>

template 인자는 참조 된 객체의 유형과 cv-qualification을 지정합니다. r2를 참조하고에 const int대한 참조 만 생성합니다 const int. const펑터가있는 참조 래퍼에 대한 호출은 const멤버 함수 만 호출 합니다 operator().

Rvalue 이니셜 라이저는 허용하면 득보다 해를 끼칠 수 있으므로 허용되지 않습니다. rvalue는 어쨌든 이동 될 것이기 때문에 (그리고 부분적으로 피할 수 있는 보장 된 복사 제거 와 함께 ) 의미 체계를 개선하지 않습니다. 참조 래퍼가 pointee의 수명을 연장하지 않기 때문에 매달린 포인터를 도입 할 수 있습니다.

도서관 상호 작용

앞에서 언급했듯이 해당 인수를 a를 통해 전달하여 make_tuple결과 tuple에 참조를 저장 하도록 지시 할 수 있습니다 reference_wrapper.

int i;
auto t1 = std::make_tuple(i); // Copies i. Type of t1 is tuple<int>
auto t2 = std::make_tuple(std::ref(i)); // Saves a reference to i.
                                        // Type of t2 is tuple<int&>

forward_as_tuple다음 과 약간 다릅니다 . 여기서, rvalue는 인수로 사용할 수 없습니다.

std::bind동일한 동작을 보여줍니다. 인수를 복사하지 않고 reference_wrapper. 해당 인수 (또는 functor!)를 복사 할 필요는 bind없지만 -functor가 사용되는 동안 범위에 남아있는 경우 유용합니다 .

일반 포인터와의 차이점

  • 추가적인 수준의 구문 적 간접 지정은 없습니다. 포인터가 참조하는 객체에 대한 lvalue를 얻으려면 포인터를 역 참조해야합니다. reference_wrapper에는 암시 적 변환 연산자 가 있으며 래핑 된 객체처럼 호출 될 수 있습니다.

    int i;
    int& ref = std::ref(i); // Okay
  • reference_wrappers는 포인터와 달리 null 상태가 아닙니다. 그것들은 참조 또는 다른reference_wrapper 것으로 초기화되어야 합니다 .

    std::reference_wrapper<int> r; // Invalid
  • 유사점은 얕은 복사 의미 체계입니다. 포인터와 reference_wrappers를 다시 할당 할 수 있습니다.


답변

최소한 두 가지 동기 부여 목적이 있습니다 std::reference_wrapper<T>.

  1. 함수 템플릿에 값 매개 변수로 전달 된 객체에 참조 의미를 부여하는 것입니다. 예를 들어, std::for_each()값으로 함수 객체 매개 변수 를 사용 하는 전달하려는 큰 함수 객체가있을 수 있습니다 . 개체 복사를 방지하려면 다음을 사용할 수 있습니다.

    std::for_each(begin, end, std::ref(fun));

    표현식 std::reference_wrapper<T>에 대한 인수를 전달 하는 std::bind()것은 값이 아닌 참조로 인수를 바인딩하는 데 매우 일반적입니다.

  2. 해당 튜플 요소 std::reference_wrapper<T>와 함께 사용하면 std::make_tuple()a T&가 아닌 a가됩니다 T.

    T object;
    f(std::make_tuple(1, std::ref(object)));

답변

자체 문서화 코드의 또 다른 차이점은을 사용하면 reference_wrapper본질적으로 객체의 소유권을 거부한다는 것입니다. 반대로, a unique_ptr는 소유권을 주장하지만, 베어 포인터는 소유 할 수도 있고 아닐 수도 있습니다 (많은 관련 코드를 살펴 보지 않고는 알 수 없습니다).

vector<int*> a;                    // the int values might or might not be owned
vector<unique_ptr<int>> b;         // the int values are definitely owned
vector<reference_wrapper<int>> c;  // the int values are definitely not owned

답변

컨테이너에서 사용할 수 있도록 참조를 둘러싼 편리한 래퍼로 생각할 수 있습니다.

std::vector<std::reference_wrapper<T>> vec; // OK - does what you want
std::vector<T&> vec2; // Nope! Will not compile

그것은 기본적으로 A의 CopyAssignable버전 T&. 참조를 원할 때마다 할당 가능해야 std::reference_wrapper<T>하거나 도우미 기능을 사용해야 합니다 std::ref(). 또는 포인터를 사용하십시오.


기타 단점 : sizeof:

sizeof(std::reference_wrapper<T>) == sizeof(T*) // so 8 on a 64-bit box
sizeof(T&) == sizeof(T) // so, e.g., sizeof(vector<int>&) == 24

그리고 비교 :

int i = 42;
assert(std::ref(i) == std::ref(i)); // ok

std::string s = "hello";
assert(std::ref(s) == std::ref(s)); // compile error