How does std :: move () transfer values ​​to RValues?

I just found that I did not fully understand the logic of std::move() .

Firstly, I googled, but it seems that there are only documents on how to use std::move() , and not how its structure works.

I mean, I know what a member function is, but when I look at the definition of std::move() in VS2010, it is still confused.

the definition of std :: move () goes below.

 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); } 

What is strange to me is the parameter, (_Ty && _Arg), because when I call the function, as you see below,

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

it is basically equal

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

But, as you already know, you cannot directly link LValue to an RValue link, which makes me think that this should be so.

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

However, this is absurd, because std :: move () should work for all values.

So, I guess, to fully understand how this works, I should also take a look at these structures.

 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; }; 

Unfortunately, he is still confused, and I do not understand.

I know that this is all due to the lack of basic syntax skills in C ++. I would like to know how these works are thoroughly and any documents that I can get on the Internet will be more than welcome. (If you can just explain it, that would be awesome too).

+64
c ++ c ++ 11 move-semantics
Sep 22 '11 at 6:00
source share
2 answers

Let's start with the move function (which I cleaned up a bit):

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



Let's start with the simpler part - that is, when a function is called with rvalue:

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

and our move template gets an instance as follows:

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

Since remove_reference converts T& to T or T&& to T , and Object not a reference, our final function:

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

Now you might think: do we even need a cast? Answer: yes, we do. The reason is simple; the specified rvalue reference is treated as an lvalue (and the implicit conversion from lvalue to rvalue reference is prohibited by the standard).




This is what happens when we call move with an lvalue:

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

and the corresponding move instance:

 // 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 move on to the difficult part: what does Object& && mean and how can it communicate with lvalue?

To ensure perfect forwarding, the C ++ 11 standard provides special rules for collapsing links, which are as follows:

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

As you can see, according to these rules, Object& && actually means Object& , which is a simple lvalue reference that allows you to bind lvalues.

Final function:

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

which is not like the previous instance with rvalue - they both pass their argument to the rvalue reference and then return it. The difference is that the first instance can only be used with rvalues, and the second with lvalues.




To explain why we need remove_reference little more, try this function

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

and create it using lvalue.

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

Applying the above rules for dropping links, you can see that we get a function that is not suitable for use as move (just, you call it lvalue, you get lvalue back). In any case, this function is an identity function.

 Object& wanna_be_move(Object& arg) { return static_cast<Object&>(arg); } 
+107
Sep 22 '11 at 16:39
source share

_Ty is a template parameter, and in this situation

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

_Ty is the type "Object &"

therefore _Remove_reference is needed.

It will be more like

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

If we didn’t remove the link, it would be similar to what we did

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

But ObjectRef && comes down to Object &, which we could not associate with obj2.

The reason this happens is to support perfect forwarding. See this document .

+2
Sep 22 '11 at 6:12
source share



All Articles