auto &&는 무엇을 알려줍니까? var합니까? 우리의 자원을 훔칠

당신이 같은 코드를 읽는다면

auto&& var = foo();

여기서 footype 값으로 반환되는 함수가 T있습니다. 그런 다음 varrvalue 참조 유형의 lvalue는 T입니다. 그러나 이것은 무엇을 의미 var합니까? 우리의 자원을 훔칠 수 있다는 의미 var입니까? 독점 소유권을 가지고 있음을 알리기 위해 auto&&a unique_ptr<>를 반환 할 때와 같이 코드를 독자에게 알리기 위해 사용해야하는 합리적인 상황 이 있습니까? 그리고 예를 들어 대해 무엇을 T&&할 때 T클래스 유형입니까?

auto&&템플릿 프로그래밍에서 사용하는 것 이외의 다른 사용 사례가 있는지 이해하고 싶습니다 . 이 문서의 예에서 논의 된 것과 같은 범용 참조 스콧 마이어스에 의해.



답변

사용하여 auto&& var = <initializer>: 당신이 말을 내가 관계없이 좌변 또는를 rvalue 표현인지의 이니셜을 수락하고 나는 그것의 const와 보존됩니다 . 일반적으로 전달하는 데 사용됩니다 (일반적으로와 함께 T&&). 는 “보편적 참조”때문에이 작품 이유는 auto&&T&&에 바인딩 아무것도 .

당신은 말할 수도 있습니다. const auto&왜냐하면 그것이 무엇 에도 바인딩 될 것이기 때문에 왜 사용하지 않습니까? const참조 를 사용할 때의 문제 는 그것이 참조라는 것입니다 const! 나중에 비 const 참조에 바인딩하거나 표시되지 않은 멤버 함수를 호출 할 수 없습니다 const.

예를 들어,을 가져 std::vector오고 반복자를 첫 번째 요소로 가져 와서 반복자가 가리키는 값을 어떤 방식으로 수정한다고 가정하십시오.

auto&& vec = some_expression_that_may_be_rvalue_or_lvalue;
auto i = std::begin(vec);
(*i)++;

이 코드는 이니셜 라이저 표현식에 관계없이 잘 컴파일됩니다. auto&&다음과 같은 방법으로 실패 할 수있는 대안 :

auto         => will copy the vector, but we wanted a reference
auto&        => will only bind to modifiable lvalues
const auto&  => will bind to anything but make it const, giving us const_iterator
const auto&& => will bind only to rvalues

이를 위해 auto&&완벽하게 작동합니다! auto&&이와 같이 사용하는 예는 범위 기반 for루프에 있습니다. 자세한 내용은 다른 질문 을 참조하십시오.

그런 다음 사용하는 경우 std::forward사용자에 auto&&원래 좌변 또는를 rvalue 중 하나라는 사실을 보존하기 위해 참조 코드는 말합니다 : 지금은 좌변 또는를 rvalue 표현 중 하나에서 개체를 가지고 있음을, 내가 원래 그것을 valueness 중 보존 할 내가 가장 효율적으로 사용할 수 있도록 했으므로 무효가 될 수 있습니다. 에서처럼 :

auto&& var = some_expression_that_may_be_rvalue_or_lvalue;
// var was initialized with either an lvalue or rvalue, but var itself
// is an lvalue because named rvalues are lvalues
use_it_elsewhere(std::forward<decltype(var)>(var));

이렇게하면 use_it_elsewhere원래 이니셜 라이저가 수정 가능한 rvalue 일 때 성능을 위해 (복사를 피하기 위해) 내장을 제거 할 수 있습니다.

우리가 언제 자원을 훔칠 수 있는지에 대한 의미는 무엇입니까 var? auto&&의지가 무엇이든 구속력이 있기 때문에 , 우리는 아마도 var자신의 용기 를 찢어 버릴 수는 없습니다. 그것은 아주 가치가 있거나 구성 요소 일 수도 있습니다. 그러나 std::forward내부를 완전히 손상시킬 수있는 다른 기능에 대해서도 가능합니다. 이 작업을 수행하자마자 var잘못된 상태에있는 것으로 간주 해야합니다.

이제 auto&& var = foo();질문에 주어진 것처럼 foo T를 값으로 반환 합니다. 이 경우 우리는의 유형 var이로 추론 됨을 확실히 알고 있습니다 T&&. 우리는 그것이 rvalue라는 것을 확실히 알고 있기 때문에 우리 std::forward는 자원을 훔칠 수있는 권한이 필요하지 않습니다 . 이 특정한 경우에, 그 아는 foo값으로 반환 :로, 독자는 그것을 읽어야 내가로부터 돌려 임시로를 rvalue 참조 데려 갈거야 foo내가 행복하게에서 이동할 수 있습니다.


부록으로서, some_expression_that_may_be_rvalue_or_lvalue“코드가 잘 변경 될 수있는”상황 이외 의 표현이 나타날 때 언급 할 가치가 있다고 생각합니다 . 다음은 좋은 예입니다.

std::vector<int> global_vec{1, 2, 3, 4};

template <typename T>
T get_vector()
{
  return global_vec;
}

template <typename T>
void foo()
{
  auto&& vec = get_vector<T>();
  auto i = std::begin(vec);
  (*i)++;
  std::cout << vec[0] << std::endl;
}

여기 get_vector<T>()에 제네릭 형식에 따라 lvalue 또는 rvalue가 될 수있는 멋진 표현이 있습니다 T. 기본적으로 get_vector의 템플릿 매개 변수를 통해 반환 유형을 변경합니다 foo.

우리가 호출 할 때 foo<std::vector<int>>, get_vector리턴 global_vec를 rvalue 식을 제공 값에 의해. 또는를 호출 foo<std::vector<int>&>하면 참조로 get_vector반환 global_vec되어 lvalue 표현식이 생성됩니다.

우리가 할 경우 :

foo<std::vector<int>>();
std::cout << global_vec[0] << std::endl;
foo<std::vector<int>&>();
std::cout << global_vec[0] << std::endl;

예상대로 다음과 같은 결과가 나타납니다.

2
1
2
2

당신이를 변경한다면 auto&&어떤에 코드에서 auto, auto&, const auto&, 또는 const auto&&우리는 우리가 원하는 결과를 얻을 수 없습니다.


auto&&참조가 lvalue 또는 rvalue 표현식으로 초기화 되는지 여부에 따라 프로그램 논리를 변경하는 다른 방법 은 유형 특성을 사용하는 것입니다.

if (std::is_lvalue_reference<decltype(var)>::value) {
  // var was initialised with an lvalue expression
} else if (std::is_rvalue_reference<decltype(var)>::value) {
  // var was initialised with an rvalue expression
}

답변

먼저, 나는 이 참고 답변 을 보편적 참조를위한 템플릿 인수 추론이 어떻게 작동하는지에 대한 단계별 설명을 읽어 보는 것이 좋습니다 .

우리의 자원을 훔칠 수 있다는 의미 var입니까?

반드시 그런 것은 아닙니다. 어떤 경우 foo()의 모든 갑자기 참조를 반환하거나 전화를 변경하지만의 사용을 업데이트하는 것을 잊었다 var? 또는 일반 코드를 사용하고 foo()매개 변수에 따라 반환 유형 이 변경 될 수 있습니까?

auto&&정확히 거의 이므로 T&&in 과 정확히 동일하다고 생각하십시오 . 함수를 전달하거나 어떤 식 으로든 사용해야 할 때 함수의 범용 참조로 무엇을합니까? 원래 값 범주를 다시 얻는 데 사용 합니다. 함수에 전달되기 전에 lvalue 인 경우를 통과 한 후에 lvalue를 유지합니다 . 이것이 rvalue 인 경우 다시 rvalue가됩니다 (명명 된 rvalue 참조는 lvalue입니다).template<class T> void f(T&& v);std::forward<T>(v)std::forward

그렇다면 var일반적인 방식으로 올바르게 사용하는 방법은 무엇입니까? 사용하십시오 std::forward<decltype(var)>(var). 이것은 std::forward<T>(v)위의 함수 템플릿 과 정확히 동일하게 작동합니다 . varis 인 경우 T&&rvalue가 반환되고이면 T&lvalue가 반환됩니다.

그래서, 주제에 다시 : 무슨 auto&& v = f();std::forward<decltype(v)>(v)코드베이스에서 우리에게? 그들은 v가장 효율적인 방법으로 획득되고 전달 될 것이라고 말합니다 . 그러나 그러한 변수를 전달한 후에는 이동할 수 있으므로 재설정하지 않고 더 이상 잘못 사용할 수 있습니다.

개인적으로, 나는 사용 auto&&내가 필요로 할 때 일반적인 코드 modifyable 변수를. 이동 작업이 잠재적으로 내장을 훔치기 때문에 rvalue를 완벽하게 전달하는 것은 수정됩니다. 내가 게으 르기를 원한다면 (즉, 유형 이름의 철자를 쓰지 않아도 됨) 수정 할 필요가없는 경우 (예 : 범위의 요소 만 인쇄 할 때)에 붙어 auto const&있습니다.


†는 auto이 지금까지 다른 인 auto v = {1,2,3};vstd::initializer_list하는 동안, f({1,2,3})공제 실패 할 것이다.


답변

T이동 생성자가있는 유형 을 고려 하고 가정하십시오.

T t( foo() );

해당 이동 생성자를 사용합니다.

이제 다음에서 리턴을 캡처하기 위해 중간 참조를 사용하겠습니다 foo.

auto const &ref = foo();

이것은 이동 생성자를 사용하지 않기 때문에 반환 값을 이동하는 대신 복사해야합니다 ( std::move여기서 사용하더라도 실제로 const 참조를 이동할 수는 없습니다)

T t(std::move(ref));   // invokes T::T(T const&)

그러나 우리가

auto &&rvref = foo();
// ...
T t(std::move(rvref)); // invokes T::T(T &&)

이동 생성자는 여전히 사용 가능합니다.


그리고 다른 질문들을 다루기 위해 :

… auto &&를 사용하여 독자에게 코드를 알려 주어야 할 합리적인 상황이 있습니까?

Xeo가 말한 첫 번째 것은 본질적으로 X 유형에 관계없이 가능한 한 효율적으로 X를 전달 한다는 것 입니다. 따라서 auto&&내부적 으로 사용되는 코드를 보는 것은 적절한 경우 내부적으로 이동 의미론을 사용한다는 것을 알려 주어야합니다.

… unique_ptr <>을 반환하여 독점 소유권을 가지고 있다고 말할 때처럼 …

함수 템플릿이 type 인수를 사용 T&&하면 전달한 객체를 이동할 수 있다는 의미입니다. 반환하면 unique_ptr명시 적으로 호출자에게 소유권이 부여됩니다. 수락 하면 발신자의 소유권 T&&제거 될 수 있습니다 (이동 ctor가있는 경우 등).


답변

auto &&구문은 C ++ 11의 두 가지 새로운 기능을 사용합니다.

  1. auto부분을 ​​통해 컴파일러는 컨텍스트 (이 경우 반환 값)를 기반으로 형식을 추론 할 수 있습니다. 이것은 (당신이 원하는 여부를 지정할 수있는 기준 자격없이 T, T &또는 T &&추정되는 유형 T).

  2. &&새로운 이동 의미입니다. 이동 시맨틱을 지원하는 유형 T(T && other)은 새 유형으로 컨텐츠를 최적으로 이동 시키는 생성자 를 구현합니다 . 이를 통해 객체는 깊은 복사를 수행하는 대신 내부 표현을 교체 할 수 있습니다.

이를 통해 다음과 같은 것을 가질 수 있습니다.

std::vector<std::string> foo();

그래서:

auto var = foo();

반환 된 벡터의 사본을 수행하지만 (비싸지 만)

auto &&var = foo();

벡터의 내부 표현 (벡터 foo와 빈 벡터 var) 을 교체 하므로 속도가 더 빠릅니다.

이것은 새로운 for-loop 구문에서 사용됩니다.

for (auto &item : foo())
    std::cout << item << std::endl;

for-loop가에서 auto &&~에 대한 반환 값을 보유 foo하고 item있으며의 각 값에 대한 참조입니다 foo.