std::move 如何将表达式转换为右值?
我不完全理解 std::move()
的实现。 也就是说,我对 MSVC 标准库中的这种实现感到困惑:
模板
排队 类型名 tr1::_Remove_reference<_Ty>::_Type&& 移动(_Ty&&_Arg) { // 将 _Arg 转发为可移动 return ((typename tr1::_Remove_reference<_Ty>::_Type&&)_Arg); }
当我像这样调用 std::move
时:
Object obj1;
Object obj2 = std::move(obj1); // _Ty&& _Arg binds to obj1
... _Arg
引用参数绑定到左值 obj1
。 您不能直接将右值引用绑定到左值,这让我认为需要强制转换为像 (Object&&)
这样的右值引用。 然而,这是荒谬的,因为 std::move()
必须适用于所有值。
为了完全理解它是如何工作的,我也研究了 std::remove_reference
的实现:
模板
结构体_Remove_reference { // 删除引用 typedef _Ty _Type; }; 模板<类_Ty> 结构_Remove_reference<_Ty> { // 删除引用 typedef _Ty _Type; }; 模板<类_Ty> 结构体_Remove_reference<_Ty&> { // 删除右值引用 typedef _Ty _Type; };
不幸的是,它仍然令人困惑,我不明白。 请帮助我理解 std::move
的实现。
I don't fully understand the implementation of std::move()
.
Namely, I am confused by this implementation in the MSVC standard library:
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); }
When I call std::move
like this:
Object obj1;
Object obj2 = std::move(obj1); // _Ty&& _Arg binds to obj1
... the _Arg
reference parameter binds to the lvalue obj1
.
You cannot directly bind an rvalue reference to an lvalue, which makes me think that a cast to an rvalue reference like (Object&&)
is required.
However, this is absurd because std::move()
must work for all the values.
To fully understand how this works, I've looked at the implementation of std::remove_reference
too:
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 it's still as confusing and I don't get it.
Please help me understand the implementation of std::move
.
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(2)
我们从 move 函数开始(我对其进行了一些清理):
让我们从更简单的部分开始 - 也就是说,当使用 rvalue: 调用该函数时
,我们的
move
模板将被实例化,如下所示:由于
remove_reference
将T&
转换为T
或T&&
转换为T
,而Object
不是参考,我们的最终函数是:现在,您可能想知道:我们还需要演员阵容吗?答案是:是的,我们愿意。原因很简单;命名右值引用被视为左值(标准禁止从左值到右值引用的隐式转换)。
时发生的情况
以下是当我们使用左值调用
move
和相应的move
实例化:同样,
remove_reference
将Object&
转换为Object
我们得到:现在我们到了棘手的部分:
Object& 是什么? &&
甚至意味着它如何绑定到左值?为了实现完美的转发,C++11标准为引用折叠提供了特殊的规则,如下所示:
如您所见,在这些规则下
Object& &&
实际上意味着Object&
,它是允许绑定左值的普通左值引用。最终函数是这样的:
这与之前使用右值的实例化没有什么不同 - 它们都将其参数转换为右值引用,然后返回它。不同之处在于,第一个实例化只能与右值一起使用,而第二个实例化则与左值一起使用。
为了解释为什么我们还需要
remove_reference
,让我们尝试一下这个函数并用左值实例化它。
应用上面提到的引用折叠规则,你可以看到我们得到了无法使用
move
的函数(简单来说,你用左值调用它,你会得到左值)。如果有的话,这个函数就是恒等函数。We start with the move function (which I cleaned up a little bit):
Let's start with the easier part - that is, when the function is called with rvalue:
and our
move
template gets instantiated as follows:Since
remove_reference
convertsT&
toT
orT&&
toT
, andObject
is not reference, our final function is: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:and corresponding
move
instantiation:Again,
remove_reference
convertsObject&
toObject
and we get: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:
As you can see, under these rules
Object& &&
actually meansObject&
, which is plain lvalue reference that allows binding lvalues.Final function is thus:
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 functionand instantiate it with lvalue.
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._Ty 是模板参数,在这种情况下
_Ty 是类型“Object &”
这就是为什么 _Remove_reference 是必要的。
But
如果我们没有删除引用,它会更像我们正在做的那样
ObjectRef&&简化为 Object &,我们无法将其绑定到 obj2。
之所以减少这种方式,是为了支持完美转发。请参阅本文。
_Ty is a template parameter, and in this situation
_Ty is type "Object &"
which is why the _Remove_reference is necessary.
It would be more like
If we didn't remove the reference it would be like we were doing
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.