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

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-28 17:03:34  来源:igfitidea点击:

How does std::move() transfer values into RValues?

c++c++11move-semantics

提问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 movetemplate 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_referenceconverts T&to Tor T&&to T, and Objectis not reference, our final function is:

由于remove_reference转换T&TT&&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 movewith lvalue:

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

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

and corresponding moveinstantiation:

和相应的move实例化:

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

Again, remove_referenceconverts Object&to Objectand 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_referencea 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.

之所以减少这种方式,是为了支持完美转发。请参阅本文