什么使移动对象比复制更快? [英] What makes moving objects faster than copying?

查看:174
本文介绍了什么使移动对象比复制更快?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我听到斯科特迈尔斯说的std ::移动()不动任何东西......但我不明白这意味着什么。

I have heard Scott Meyers say "std::move() doesn't move anything" ... but I haven't understood what it means.

因此,为了指定我的问题,请考虑以下内容:

So to specify my question consider the following:

class Box { /* things... */ };

Box box1 = some_value;
Box box2 = box1;    // value of box1 is copied to box2 ... ok

什么:

Box box3 = std::move(box1);



我明白左值和右值,但规则是什么,我不明白是什么是实际发生的事情在内存中?它只是以一些不同的方式复制值,共享地址或什么?更具体地讲:是什么让移动速度比快复制

I do understand the rules of lvalue and rvalue but what I don't understand is what is actually happening in the memory? Is it just copying the value in some different way, sharing an address or what? More specifically: what makes moving faster than copying?

我只是觉得,理解这将让一切都清楚我吗? !在此先感谢

I just feel that understanding this would make everything clear to me. Thanks in advance!

编辑:请注意,我不要求有关的std ::移动() 实施或任何语法的东西。

Please note that I'm not asking about the std::move() implementation or any syntactic stuff.

推荐答案

由于@gudok之前回答,一切都在执行。 。然后一个位是用户代码。

As @gudok answered before, everything is in the implementation... Then a bit is in user code.

让我们假设我们正在谈论的。拷贝构造函数赋值给当前类

Let's assume we're talking about the copy-constructor to assign a value to the current class.

您将提供将考虑两种情况的实现:

The implementation you'll provide will take into account two cases:


  1. 该参数是一个l值,因此您无法触摸它,根据定义

  2. 该参数是一个r值,因此


  1. the parameter is a l-value, so you can't touch it, by definition
  2. the parameter is a r-value, so, implicitly, the temporary won't live much longer beyond you using it, so, instead of copying its content, you could steal its content



















两者都是使用重载实现的:

Both are implemented using an overload:

Box::Box(const Box & other)
{
   // copy the contents of other
}

Box::Box(Box && other)
{
   // steal the contents of other
}



< h3> light classes的实现

假设你的类包含两个整数:你不能那些,因为它们是纯原始值。 似乎之类的唯一东西就是复制这些值,然后将原始值设置为零,或者类似这样的东西...这对于简单整数没有意义。为什么要做额外的工作?

The implementation for light classes

Let's say your class contains two integers: You can't steal those because they are plain raw values. The only thing that would seem like stealing would be to copy the values, then set the original to zero, or something like that... Which makes no sense for simple integers. Why do that extra work?

因此对于轻值类,实际上提供两个特定的实现,一个用于l值,一个用于r值,没有意义。

So for light value classes, actually offering two specific implementations, one for l-value, and one for r-values, makes no sense.

只提供l值实现就足够了。

Offering only the l-value implementation will be more than enough.

但是在一些重类(例如std :: string,std :: map等)的情况下,复制意味着潜在的成本,通常在分配。所以,理想情况下,你想尽可能避免它。

But in the case of some heavy classes (i.e. std::string, std::map, etc.), copying implies potentially a cost, usually in allocations. So, ideally, you want to avoid it as much as possible. This is where stealing the data from temporaries becomes interesting.

假设您的Box包含一个指向 HeavyResource 的原始指针, / code>,这是复制成本高。代码变为:

Assume your Box contains a raw pointer to a HeavyResource that is costly to copy. The code becomes:

Box::Box(const Box & other)
{
   this->p = new HeavyResource(*(other.p)) ; // costly copying
}

Box::Box(Box && other)
{
   this->p = other.p ; // trivial stealing, part 1
   other.p = nullptr ; // trivial stealing, part 2
}

这是一个简单的构造函数,需要分配)比另一个(移动构造函数,只需要指定原始指针的分配)慢得多。

It's plain one constructor (the copy-constructor, needing an allocation) is much slower than another (the move-constructor, needing only assignements of raw pointers).

事情是:默认情况下,只有当参数是临时的时候,编译器才会调用快速代码(这是一个更微妙的,但是忍受我...) )。

The thing is: By default, the compiler will invoke the "fast code" only when the parameter is a temporary (it's a bit more subtle, but bear with me...).

为什么?

因为编译器可以保证你可以从某个对象中偷取任何问题,如果该对象是临时的(或将被销毁很快之后)。对于其他对象,偷取意味着你突然有一个空或不完整对象,这仍然可以在代码中进一步使用。导致崩溃或错误:

Because the compiler can guarantee you can steal from some object without any problem only if that object is a temporary (or will be destroyed soon after anyway). For the other objects, stealing means you suddenly have an "empty", or "incomplete" object, which could be still used further down in the code. Leading to crashes or bugs:

Box box3 = static_cast<Box &&>(box1); // calls the "stealing" constructor
box1.doSomething();         // Oops! You are using an "empty" object!

但有时候,你想要的性能。

But sometimes, you want the performance. So, how do you do it?

如您所写:

Box box1 = some_value;
Box box2 = box1;            // value of box1 is copied to box2 ... ok
Box box3 = std::move(box1); // ???

对box2会发生什么,因为box1是一个l值,第一个slow复制构造函数。这是正常的C ++ 98代码。

What happens for box2 is that, as box1 is a l-value, the first, "slow" copy-constructor is invoked. This is the normal, C++98 code.

现在,对于box3,有趣的发生:std :: move返回相同的box1,值引用,而不是l值。所以行:

Now, for box3, something funny happens: The std::move does return the same box1, but as a r-value reference, instead of a l-value. So the line:

Box box3 = ...

...不会调用box1上的复制构造函数。

... will NOT invoke copy-constructor on box1.

它将调用INSTEAD的偷建构函数作为移动构造函数)在box1上。

It will invoke INSTEAD the stealing constructor (officially known as the move-constructor) on box1.

由于Box的move构造函数的实现会偷取box1的内容, box1为空,box3包含box1的(上一个)内容。

And as your implementation of the move constructor for Box does "steal" the content of box1, at the end of the expression, box1 is empty, and box3 contains the (previous) content of box1.

当然,在l值上写std :: move意味着你承诺不再使用该l值(除非你再次在其中一个有效的值)。编译器不能帮助你。

Of course, writing std::move on a l-value means you make a promise you won't use that l-value again (unless you put another valid value inside it again). The compiler can't help you much, there.

如上所述,std :: move does 没有。它只告诉编译器:你看到l值,请考虑它的r值,只是一秒钟。

As said above, the std::move does nothing. It only tells the compiler: "You see that l-value? please consider it a r-value, just for a second".

所以,在:

Box box3 = std::move(box1); // ???

...用户代码(即std :: move)告诉编译器参数可以考虑为这个表达式的r值,因此,将调用move构造函数。

... the user code (i.e. the std::move) tells the compiler the parameter can be considered as a r-value for this expression, and thus, the move constructor will be called.

对于代码作者(和代码审查者),代码实际上告诉它是确定窃取box1的内容,将其移动到box3。然后,代码作者将必须确保box1不再在空状态中使用。这是他们的责任。

For the code author (and the code reviewer), the code actually tells it is ok to steal the content of box1, to move it into box3. The code author will then have to make sure box1 is not used anymore in that "empty" state. It is their responsibility.

但最终,它是实现移动构造函数,将有所作为,主要是在性能:如果移动构造函数实际上偷了内容的r值,那么你会看到一个差异。如果它做任何其他事情,那么作者谎言,但这是另一个问题...

But in the end, it is the implementation of the move constructor that will make a difference, mostly in performance: If the move constructor actually steals the content of the r-value, then you will see a difference. If it does anything else, then the author lied about it, but this is another problem...

这篇关于什么使移动对象比复制更快?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆