c++ - std::move() 如何将值传输到 RValues 中?

标签 c++ c++11 move-semantics

我发现自己没有完全理解std::move()的逻辑。

起初,我搜索了一下,但似乎只有关于如何使用 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++ 的基本语法技能。 我想知道这些是如何彻底工作的,并且我可以在互联网上获得的任何文件都将受到欢迎。 (如果你能解释一下,那也太棒了)。

最佳答案

我们从 move 函数开始(我稍微清理了一下):

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

让我们从更简单的部分开始——即使用右值调用函数时:

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

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_referenceObject& 转换为 Object,我们得到:

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

现在我们进入棘手的部分:Object& && 是什么意思,它如何绑定(bind)到左值?

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

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

如您所见,在这些规则下,Object& && 实际上意味着 Object&,它是允许绑定(bind)左值的普通左值引用。

最终的功能是:

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

这与之前使用 rvalue 的实例化没有什么不同——它们都将其参数转换为 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);
}

应用上面提到的引用折叠规则,你可以看到我们得到的函数不能作为 move 使用(简单地说,你用左值调用它,你得到左值)。如果有的话,这个函数就是恒等函数。

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

关于c++ - std::move() 如何将值传输到 RValues 中?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/7510182/

相关文章:

c++ - 无法获得处理器亲和性 (linux)

c++ - 为什么内联未命名的命名空间?

c++11 - C++ 仿函数通过递归 : "attempt to use a deleted function"

c++ - 说一个C++对象是可 move 的,这到底意味着什么?

c++ - 语义略有不同的复制构造函数和赋值运算符中是否存在陷阱?

c++ - 圆形无锁缓冲区

c++ - 这是错字还是我遗漏了什么?

c++ - 当在右值引用上调用 std::move 时会发生什么?

c++ - 使用 std::move 实现 "take"方法

c++函数将time_t格式化为std::string:缓冲区长度?