C++ std::move() 如何将值传输到 RValues 中?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/7510182/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
How does std::move() transfer values into RValues?
提问by Dean Seo
I just found myself not fully understanding the logic of std::move()
.
我只是发现自己没有完全理解std::move()
.
At first, I googled it but seems like there are only documents about how to use std::move()
, not how its structure works.
起初,我用谷歌搜索了它,但似乎只有关于如何使用的文档std::move()
,而不是它的结构如何工作。
I mean, I know what the template member function is but when I look into std::move()
definition in VS2010, it is still confusing.
我的意思是,我知道模板成员函数是什么,但是当我查看std::move()
VS2010 中的定义时,它仍然令人困惑。
the definition of std::move() goes below.
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);
}
What is weird first to me is the parameter, (_Ty&& _Arg), because when I call the function like you see below,
对我来说最奇怪的是参数 (_Ty&& _Arg),因为当我像下面看到的那样调用函数时,
// main()
Object obj1;
Object obj2 = std::move(obj1);
it basically equals to
它基本上等于
// std::move()
_Ty&& _Arg = Obj1;
But as you already know, you can not directly link a LValue to a RValue reference, which makes me think that it should be like this.
但是正如你已经知道的,你不能直接将一个 LValue 链接到一个 RValue 引用,这让我觉得它应该是这样的。
_Ty&& _Arg = (Object&&)obj1;
However, this is absurd because std::move() must work for all the values.
然而,这是荒谬的,因为 std::move() 必须适用于所有值。
So I guess to fully understand how this works, I should take a look at these structs 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.
不幸的是,它仍然令人困惑,我不明白。
I know that this is all because of my lack of basic syntax skills about C++. I'd like to know how these work thoroughly and any documents that I can get on the internet will be more than welcomed. (If you can just explain this, that will be awesome too).
我知道这都是因为我缺乏有关 C++ 的基本语法技能。我想知道这些是如何彻底工作的,我可以在互联网上获得任何文件,我都非常欢迎。(如果你能解释这一点,那也太棒了)。
回答by Vitus
We start with the move function (which I cleaned up a little bit):
我们从 move 函数开始(我稍微清理了一下):
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 easier part - that is, when the function is called with rvalue:
让我们从更简单的部分开始——也就是说,当使用右值调用函数时:
Object a = std::move(Object());
// Object() is temporary, which is prvalue
and our move
template gets instantiated as follows:
我们的move
模板被实例化如下:
// 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
is not reference, our final function is:
由于remove_reference
转换T&
到T
或T&&
至T
,而Object
不是参考,我们的最终功能是:
Object&& move(Object&& arg)
{
return static_cast<Object&&>(arg);
}
Now, you might wonder: do we even need the cast? The answer is: yes, we do. The reason is simple; named rvalue reference istreated as lvalue (and implicit conversion from lvalue to rvalue reference is forbidden by standard).
现在,您可能想知道:我们甚至需要演员阵容吗?答案是:是的,我们有。原因很简单;命名的右值引用被视为左值(标准禁止从左值到右值引用的隐式转换)。
Here's what happens when we call move
with lvalue:
当我们move
使用左值调用时会发生以下情况:
Object a; // a is lvalue
Object b = std::move(a);
and corresponding move
instantiation:
和相应的move
实例化:
// 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:
再次remove_reference
转换Object&
为Object
,我们得到:
Object&& move(Object& && arg)
{
return static_cast<Object&&>(arg);
}
Now we get to the tricky part: what does Object& &&
even mean and how can it bind to lvalue?
现在我们到了棘手的部分:它Object& &&
是什么意思,它如何绑定到左值?
To allow perfect forwarding, C++11 standard provides special rules for reference collapsing, which are as follows:
为了实现完美转发,C++11标准提供了特殊的引用折叠规则,具体如下:
Object & & = Object &
Object & && = Object &
Object && & = Object &
Object && && = Object &&
As you can see, under these rules Object& &&
actually means Object&
, which is plain lvalue reference that allows binding lvalues.
如您所见,在这些规则下Object& &&
实际上意味着Object&
,这是允许绑定左值的普通左值引用。
Final function is thus:
最终函数是这样的:
Object&& move(Object& arg)
{
return static_cast<Object&&>(arg);
}
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.
这与之前使用 rvalue 的实例化没有什么不同 - 它们都将其参数转换为 rvalue 引用,然后返回它。不同之处在于第一个实例化只能用于右值,而第二个实例化用于左值。
To explain why do we need remove_reference
a bit more, let's try this function
为了解释为什么我们需要remove_reference
更多,让我们试试这个功能
template <typename T>
T&& wanna_be_move(T&& arg)
{
return static_cast<T&&>(arg);
}
and instantiate it with lvalue.
并用左值实例化它。
// wanna_be_move [with T = Object&]
Object& && wanna_be_move(Object& && arg)
{
return static_cast<Object& &&>(arg);
}
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.
应用上面提到的引用折叠规则,你可以看到我们得到了不可用的函数move
(简单地说,你用左值调用它,你得到左值)。如果有的话,这个函数就是恒等函数。
Object& wanna_be_move(Object& arg)
{
return static_cast<Object&>(arg);
}
回答by Vaughn Cato
_Ty is a template parameter, and in this situation
_Ty 是模板参数,在这种情况下
Object obj1;
Object obj2 = std::move(obj1);
_Ty is type "Object &"
_Ty 是类型“对象 &”
which is why the _Remove_reference is necessary.
这就是为什么 _Remove_reference 是必要的。
It would be more like
会更像
typedef Object& ObjectRef;
Object obj1;
ObjectRef&& obj1_ref = obj1;
Object&& obj2 = (Object&&)obj1_ref;
If we didn't remove the reference it would be like we were doing
如果我们不删除引用,就像我们在做的那样
Object&& obj2 = (ObjectRef&&)obj1_ref;
But ObjectRef&& reduces to Object &, which we couldn't bind to obj2.
但是 ObjectRef&& 简化为 Object &,我们无法绑定到 obj2。
The reason it reduces this way is to support perfect forwarding. See this paper.
之所以减少这种方式,是为了支持完美转发。请参阅本文。