developer tip

std :: move ()는 어떻게 값을 RValues로 전송합니까?

optionbox 2020. 9. 12. 09:46
반응형

std :: move ()는 어떻게 값을 RValues로 전송합니까?


의 논리를 완전히 이해하지 못하고 std::move()있습니다.

처음에는 검색했지만 std::move()구조가 어떻게 작동하는지가 아니라 사용 방법에 대한 문서 만있는 것 같습니다 .

내 말은, 템플릿 멤버 함수가 무엇인지 알고 있지만 std::move()VS2010에서 정의를 살펴보면 여전히 혼란 스럽습니다.

std :: move ()의 정의는 다음과 같습니다.

template<class _Ty> inline
typename tr1::_Remove_reference<_Ty>::_Type&&
    move(_Ty&& _Arg)
    {   // forward _Arg as movable
        return ((typename tr1::_Remove_reference<_Ty>::_Type&&)_Arg);
    }

저에게 가장 이상한 것은 (_Ty && _Arg) 매개 변수입니다. 왜냐하면 아래와 같이 함수를 호출하면

// main()
Object obj1;
Object obj2 = std::move(obj1);

기본적으로 다음과 같습니다.

// std::move()
_Ty&& _Arg = Obj1;

그러나 이미 알고 있듯이 LValue를 RValue 참조에 직접 연결할 수는 없기 때문에 이와 같아야한다고 생각합니다.

_Ty&& _Arg = (Object&&)obj1;

그러나 std :: move ()가 모든 값에 대해 작동해야하기 때문에 이것은 어리석은 일입니다.

그래서 이것이 어떻게 작동하는지 완전히 이해하고 있다고 생각합니다.이 구조체도 살펴 봐야합니다.

template<class _Ty>
struct _Remove_reference
{   // remove reference
    typedef _Ty _Type;
};

template<class _Ty>
struct _Remove_reference<_Ty&>
{   // remove reference
    typedef _Ty _Type;
};

template<class _Ty>
struct _Remove_reference<_Ty&&>
{   // remove rvalue reference
    typedef _Ty _Type;
};

불행히도 여전히 혼란스럽고 이해하지 못합니다.

이것이 C ++에 대한 기본적인 구문 기술이 부족하기 때문이라는 것을 알고 있습니다. 나는 이것이 어떻게 철저히 작동하는지 그리고 인터넷에서 얻을 수있는 모든 문서가 환영받는 것 이상이 될 것이라는 것을 알고 싶습니다. (당신이 이것을 설명 할 수 있다면 그것도 굉장 할 것입니다).


이동 기능부터 시작합니다 (조금 정리했습니다).

template <typename T>
typename remove_reference<T>::type&& move(T&& arg)
{
  return static_cast<typename remove_reference<T>::type&&>(arg);
}

더 쉬운 부분부터 시작하겠습니다. 즉, 함수가 rvalue로 호출 될 때입니다.

Object a = std::move(Object());
// Object() is temporary, which is prvalue

우리 move는 다음과 같이 템플릿이 인스턴스화됩니다 :

// move with [T = Object]:
remove_reference<Object>::type&& move(Object&& arg)
{
  return static_cast<remove_reference<Object>::type&&>(arg);
}

이후 remove_reference변환 T&T또는 T&&T, 그리고 Object참조 아니며, 우리 최종 함수이다 :

Object&& move(Object&& arg)
{
  return static_cast<Object&&>(arg);
}

Now, you might wonder: do we even need the cast? The answer is: yes, we do. The reason is simple; named rvalue reference is treated as lvalue (and implicit conversion from lvalue to rvalue reference is forbidden by standard).


Here's what happens when we call move with lvalue:

Object a; // a is lvalue
Object b = std::move(a);

and corresponding move instantiation:

// move with [T = Object&]
remove_reference<Object&>::type&& move(Object& && arg)
{
  return static_cast<remove_reference<Object&>::type&&>(arg);
}

Again, remove_reference converts Object& to Object and we get:

Object&& move(Object& && arg)
{
  return static_cast<Object&&>(arg);
}

Now we get to the tricky part: what does Object& && even mean and how can it bind to lvalue?

To allow perfect forwarding, C++11 standard provides special rules for reference collapsing, which are as follows:

Object &  &  = Object &
Object &  && = Object &
Object && &  = Object &
Object && && = Object &&

As you can see, under these rules Object& && actually means Object&, which is plain lvalue reference that allows binding lvalues.

Final function is thus:

Object&& move(Object& arg)
{
  return static_cast<Object&&>(arg);
}

which is not unlike the previous instantiation with rvalue - they both cast its argument to rvalue reference and then return it. The difference is that first instantiation can be used with rvalues only, while the second one works with lvalues.


To explain why do we need remove_reference a bit more, let's try this function

template <typename T>
T&& wanna_be_move(T&& arg)
{
  return static_cast<T&&>(arg);
}

and instantiate it with lvalue.

// wanna_be_move [with T = Object&]
Object& && wanna_be_move(Object& && arg)
{
  return static_cast<Object& &&>(arg);
}

Applying the reference collapsing rules mentioned above, you can see we get function that is unusable as move (to put it simply, you call it with lvalue, you get lvalue back). If anything, this function is the identity function.

Object& wanna_be_move(Object& arg)
{
  return static_cast<Object&>(arg);
}

_Ty is a template parameter, and in this situation

Object obj1;
Object obj2 = std::move(obj1);

_Ty is type "Object &"

which is why the _Remove_reference is necessary.

It would be more like

typedef Object& ObjectRef;
Object obj1;
ObjectRef&& obj1_ref = obj1;
Object&& obj2 = (Object&&)obj1_ref;

If we didn't remove the reference it would be like we were doing

Object&& obj2 = (ObjectRef&&)obj1_ref;

But ObjectRef&& reduces to Object &, which we couldn't bind to obj2.

The reason it reduces this way is to support perfect forwarding. See this paper.

참고URL : https://stackoverflow.com/questions/7510182/how-does-stdmove-transfer-values-into-rvalues

반응형