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

Dea*_*Seo 92 c++ move-semantics c++11

我发现自己并没有完全理解它的逻辑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);
    }
Run Code Online (Sandbox Code Playgroud)

首先对我来说奇怪的是参数,(_Ty && _Arg),因为当我调用函数时,如下所示,

// main()
Object obj1;
Object obj2 = std::move(obj1);
Run Code Online (Sandbox Code Playgroud)

它基本上等于

// std::move()
_Ty&& _Arg = Obj1;
Run Code Online (Sandbox Code Playgroud)

但正如您已经知道的那样,您不能直接将LValue链接到RValue引用,这让我觉得它应该是这样的.

_Ty&& _Arg = (Object&&)obj1;
Run Code Online (Sandbox Code Playgroud)

但是,这是荒谬的,因为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;
};
Run Code Online (Sandbox Code Playgroud)

不幸的是,它仍然令人困惑,我不明白.

我知道这都是因为我缺乏关于C++的基本语法技巧.我想知道这些工作如何彻底,我可以在互联网上获得的任何文件都会受到欢迎.(如果你能解释一下,那也很棒).

Vit*_*tus 155

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

template <typename T>
typename remove_reference<T>::type&& move(T&& arg)
{
  return static_cast<typename remove_reference<T>::type&&>(arg);
}
Run Code Online (Sandbox Code Playgroud)

让我们从更容易的部分开始 - 也就是说,当使用rvalue调用函数时:

Object a = std::move(Object());
// Object() is temporary, which is prvalue
Run Code Online (Sandbox Code Playgroud)

我们的move模板实例化如下:

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

由于remove_reference转换T&TT&&转向T,而Object不是参考,我们的最终功能是:

Object&& move(Object&& arg)
{
  return static_cast<Object&&>(arg);
}
Run Code Online (Sandbox Code Playgroud)

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


这是我们move用左值调用时会发生的事情:

Object a; // a is lvalue
Object b = std::move(a);
Run Code Online (Sandbox Code Playgroud)

和相应的move实例化:

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

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

Object&& move(Object& && arg)
{
  return static_cast<Object&&>(arg);
}
Run Code Online (Sandbox Code Playgroud)

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

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

Object &  &  = Object &
Object &  && = Object &
Object && &  = Object &
Object && && = Object &&
Run Code Online (Sandbox Code Playgroud)

正如您所看到的,根据这些规则Object& &&实际上意味着Object&,这是允许绑定左值的普通左值引用.

因此,最终功能是:

Object&& move(Object& arg)
{
  return static_cast<Object&&>(arg);
}
Run Code Online (Sandbox Code Playgroud)

这与之前使用rvalue的实例化没有什么不同 - 它们都将其参数转换为rvalue引用然后返回它.不同之处在于,第一个实例化只能与rvalues一起使用,而第二个实例化可以与左值一起使用.


为了解释为什么我们需要remove_reference更多,让我们尝试这个功能

template <typename T>
T&& wanna_be_move(T&& arg)
{
  return static_cast<T&&>(arg);
}
Run Code Online (Sandbox Code Playgroud)

并用左值实例化它.

// wanna_be_move [with T = Object&]
Object& && wanna_be_move(Object& && arg)
{
  return static_cast<Object& &&>(arg);
}
Run Code Online (Sandbox Code Playgroud)

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

Object& wanna_be_move(Object& arg)
{
  return static_cast<Object&>(arg);
}
Run Code Online (Sandbox Code Playgroud)

  • 很好的答案.虽然我明白,对于左值,将"T"评估为"对象"是一个好主意,我不知道这是真的完成了.在这种情况下,我本来期望`T`也要评估为`Object`,因为我认为这是引入包装引用和`std :: ref`的原因,或者不是它. (3认同)
  • (没关系,在Vaughn Cato的回答中解释......) (3认同)
  • @mgiuca:如果你想让`std :: move`只将左值转换为rvalues,那么是的,`T&`就没问题了.这个技巧主要是为了灵活性:你可以在所有东西(包括rvalues)上调用`std :: move`并获得rvalue. (3认同)
  • 模板&lt;typename T&gt; void f(T arg)(这是维基百科的文章)与模板&lt;typename T&gt; void f(T&arg)之间有区别。第一个解析为值(如果要传递引用,则必须将其包装在`std :: ref`中),而第二个则始终解析为引用。令人遗憾的是,模板自变量推导的规则相当复杂,因此我无法提供精确的推理来说明“ T &amp;&amp;”为何反之于“ Object &amp;&amp;&amp;”(但确实如此)。 (2认同)