std :: move()如何将值传递给RValues?

我只是发现自己不完全理解std::move()的逻辑。

起初,我google了,但似乎只有关于如何使用std::move()文档,而不是它的结构如何工作。

我的意思是,我知道模板成员函数是什么,但是当我查看VS2010中的std::move()定义时,它仍然是令人困惑的。

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 ++的基本语法技巧。 我想知道这些工作是如何进行的,任何可以在互联网上获得的文件都会受到欢迎。 (如果你可以解释这一点,那也太棒了)。

我们从移动function开始(我清理了一下):

 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_referenceT& T转换为TT&& T ,并且Object不是引用,所以我们的最终function是:

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

现在,你可能会想:我们甚至需要演员吗? 答案是:是的,我们这样做。 原因很简单, 命名右值引用视为左值(并且左值到右值引用的隐式转换被标准禁止)。


以下是我们使用左值调用move时会发生的情况:

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

和相应的move实例:

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

再次, remove_reference转换Object& to Object ,我们得到:

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

现在我们来到棘手的部分: Object& &&甚至意味着什么?它如何绑定到左值?

为了完美转发,C ++ 11标准为参考折叠提供了特殊规则,如下所示:

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

正如你所看到的,在这些规则下Object& &&实际上意味着Object& ,它是允许绑定左值的普通左值引用。

最终的function是:

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

这与以前用rvalue实例化不一样 – 它们都将它的参数转换为右值引用,然后返回它。 区别在于第一个实例化只能用于右值,而第二个实例化只能用左值。


为了解释为什么我们需要更多的remove_reference ,我们来试试这个函数

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

并用左值实例化它。

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

应用上面提到的参考折叠规则,可以看到我们得到的函数是不可用的(简单来说,你用左值调用它,你得到左值)。 如果有的话,这个函数是身份函数。

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

_Ty是一个模板参数,在这种情况下

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

_Ty键入“Object&”

这就是为什么_Remove_reference是必要的。

它会更像

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

如果我们没有删除参考,那就好像我们正在做的那样

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

但ObjectRef &&减less到Object&,我们无法绑定到obj2。

减less这种方式的原因是为了支持完美的转发。 看到这篇文章 。