Possible Duplicate:
Can someone please explain move semantics to me?
我最近参加了一个C++ 11研讨会,并给出了如下建议。
1
| when you have && and you are unsure, you will almost always use std::move |
有人能向我解释为什么你应该使用std::move,而不是一些替代品,有些情况下你不应该使用std::move?
- @Xavierholt刚打我:不是逻辑运算符。
- 您肯定想看Scott Meyers——C++ 11中的通用引用。
- 因为,命名的右值引用实际上是lvalue。
首先,我要解决的问题可能有一个误解:每当您在代码中看到T&& t(t是实际类型,而不是模板类型)时,请记住,t的值类别是左值(引用),不再是右值(临时)。这很混乱。T&&仅仅意味着t是由一个右值为1的对象构造的,但是t本身是一个左值,而不是一个右值。如果它有一个名称(在本例中是t),那么它是一个左值,不会自动移动,但是如果它没有名称(3+4的结果),那么它是一个右值,如果可以,它会自动移动到结果中。类型(在本例中为T&&)几乎与变量的值类别(在本例中为lvalue)无关。
也就是说,如果您的代码中写入了T&& t,这意味着您有一个对临时变量的引用,如果您想销毁它是可以的。如果需要多次访问该变量,则不希望从该变量中删除cx1(1),否则它将失去其值。但你上次访问t时,如果你愿意,对std::move是安全的,对另一个t是有价值的。(95%的时候,这就是你想要做的)。所有这些也适用于auto&&变量。
1。如果t是模板类型,那么T&&是转发引用,在这种情况下,最后一次使用std::forward(t)而不是std::move(t)。看看这个问题。
- 啊哈!这就是我要找的普遍参考案例!
- @Pycthon:对于通用参考,您希望最后一次使用std::forward(t)而不是std::move(t)。
- 要记住的一点是,如果您看到一个命名变量,那么它是否声明为&&("rvalue")EDOCX1(reference)或`(copy)`(copy))并不重要,它始终被视为lvalue。您可以将真正的右值引用视为只能从函数返回的临时内容(您自己的,或者用std::move()包装,简而言之,它返回参数)。
- @卢克丹顿:我改写了,并增加了一句话来澄清价值类别与类型无关。如果它仍然是错误的,请随意编辑,或者在聊天中给我一个讲座之类的。
- @moingduck:您介意在最后一部分(std::forward vs std::move for universal references)详细描述(或发布链接)吗?
- @麦晋桁:对于一个通用的参考文献,你不知道它是一个右值还是一个左值,所以你不知道你是否需要这样做。如果输入值是右值,那么std::forward与std::move相同,如果输入值是左值,则不执行任何操作,因此始终获得正确的机制。看看这个问题。
- @穆灵鸭:啊。知道了。谢谢!:)
- 没有"普遍参考"这样的东西,唯一存在的就是参考崩溃规则。可能想解决这个问题。
- @我不同意,引用折叠规则的使用通常被称为"通用引用",这与动态分配的内存来自"堆"的方式大致相同。这不是"官方术语",但这是它的通用名称。
- 该死的,似乎只有我不能"从一个右值的物体"中得到这个,你能解释一下psyduck吗?
- @g.samaras:rvalues是表达式(函数和数学运算)的未命名结果,例如,myintx + myinty的结果本身就是int的结果,但没有名称,所以它是rvalue。可以将它传递给期望值为rvvalue的函数(&&),但不能将它传递给期望值引用的函数(&)。同样,不能将myintx传递给期望右值(&&的函数,因为它有一个名称。要"取消命名"对象,要告诉编译器移动该对象是安全的,请使用std::move函数。
- 感谢您清楚地区分这两个概念:类型和价值类别;如果不知道正确的术语,很难思考这些事情。
- "T&&仅仅意味着t可以用一个右值对象来构造"。不是必须用右值构造吗?(和ofc lvalues可以转化为rvalues,例如通过move)
- @本:修好了,谢谢。
- 另外,任何形式的auto&;&;都使用std::forward而不是std::move。
- 诺维耶克:补充说。
我发现这篇文章对于一般的右值引用的主题很有启发性。他最后提到了以东。这可能是最相关的报价:
We need to use std::move, from -- std::move is a way of
saying,"ok, honest to God I know I have an lvalue, but I want it to
be an rvalue." std::move does not, in and of itself, move anything; it
just turns an lvalue into an rvalue, so that you can invoke the move
constructor.
假设您有一个如下所示的移动构造函数:
1 2 3 4
| MyClass::MyClass(MyClass&& other): myMember(other.myMember)
{
// Whatever else.
} |
当使用语句other.myMember时,返回的值是左值。因此,代码使用复制构造函数初始化this->myMember。但由于这是一个移动构造函数,我们知道other是一个临时对象,因此它的成员也是。所以我们真的希望使用更有效的move构造函数来初始化this->myMember。使用std::move确保编译器将other.myMember视为一个右值引用,并调用move构造函数,正如您希望的那样:
1 2 3 4
| MyClass::MyClass(MyClass&& other): myMember(std::move(other.myMember))
{
// Whatever else.
} |
只是不要在需要保留的对象上使用std::move,move构造函数几乎可以保证将传递给它们的任何对象弄脏。这就是为什么他们只和临时工一起使用。
希望有帮助!
当您有一个类型为T&&的右值对象时,这意味着可以安全地移动该对象,因为以后没有其他对象依赖于其内部状态。
因为移动永远不会比复制更昂贵,所以您几乎总是希望移动它。要移动它,必须使用std::move函数。
你什么时候应该避开std::move,即使它是安全的?我不会在一些小例子中使用它,例如:
1 2
| int x = 0;
int y = std::move(x); |
除此之外,我没有看到任何负面影响。如果不使代码复杂化,那么应该尽可能地移动imho。
另一个不想移动的例子是返回值。该语言保证返回值(至少)被移动,因此不应写入
1
| return std::move(x); // not recommended |
(如果幸运的话,返回值优化点击,这比移动操作更好。)
- ScottMeyers认为你甚至应该移动原始类型。
- @FredOverflow有趣。他在C++和2012以上的对话中解释了吗?我只能把它当作某种文档来使用,但我不太相信它是一个好主意。
- 他在我联系的谈话中解释了这一点,作为对这个问题的评论。他说,"你应该在不考虑的情况下做这件事"。当然,仅仅因为斯科特·迈耶斯说了这句话,并不意味着这是每个人都必须坚持的终极真理。
当需要将对象的内容"传输"到其他地方时,可以使用"移动",而不需要进行复制。使用std::move,对象还可以在不进行复制的情况下获取临时对象的内容。
签出此链接
- "签出此链接"不是链接的好标签。你能改进一下吗?这样标签就可以描述被链接的东西了。