![]() |
1
9
我想有人可以补充说: 移动语义允许我们保留值语义,但同时在原始(复制自)对象的值对程序逻辑不重要的情况下获得引用语义的性能。 |
![]() |
2
0
受霍华德回答的启发,我写道 an article 关于这个话题,希望它能帮助那些同样对此感到疑惑的人。我把这篇文章复制/粘贴到这里。 当我学习移动语义学时,我总是有一种感觉,即使我对这个概念非常熟悉,但我无法将它融入C++的大局中。移动语义不像是为了方便而存在的语法糖,它深刻地影响了人们思考和编写C++的方式,并已成为最重要的C++习语之一。但是,嘿,C++的池塘里已经充满了其他的习惯用法,当你加入移动语义时,就会产生相互挤压。move语义是否破坏、增强或取代了其他习语?我不知道,但我想知道。 值语义价值语义学让我开始思考这个问题。由于C++中没有太多叫做“语义”的东西,我自然会想,“也许值语义和移动语义有一些联系?”。事实证明,这不仅仅是联系,而是起源:
也许您已经注意到它使用了“复制语义”这一措辞,事实上,“值语义”和“复制语义”是同一回事,我将互换使用它们。
那么什么是值语义呢?isocpp具有
whole page
从根本上讲,价值语义学意味着
赋值复制值
喜欢
相反的概念是引用语义,其中赋值复制指针。例如,在引用语义中,重要的是标识
与其他语言(Java、C#、JavaScript)不同,C++是基于值语义构建的。默认情况下,赋值执行逐位复制(如果不涉及用户定义的复制ctor),参数和返回值是复制构造的(是的,我知道有RVO)。在C++中,保持值语义被认为是一件好事。一方面,它更安全,因为你不需要担心悬空的指针和所有令人毛骨悚然的东西;另一方面,它更快,因为间接性更少,请参见 here 官方解释。 移动语义:价值语义汽车上的V8引擎移动语义并不是试图取代复制语义。它们彼此完全兼容。我想出了这个比喻,我觉得这个比喻很好地描述了他们的关系。 想象一下,你有一辆车,它带着内置的发动机运转平稳。有一天,你在这辆车上额外安装了一个V8引擎。只要你有足够的燃油,V8引擎就能加速你的汽车,这让你很高兴。 因此,汽车是值语义,V8引擎是移动语义。在车上安装引擎并不需要新车,它仍然是同一辆车,就像使用移动语义不会让你放弃值语义一样,因为你仍然在操作对象本身,而不是它的引用或指针。此外 如果可以,请移动,否则请复制 战略,由 binding preferences ,这与选择发动机的方式完全相同,即如果可以(燃油充足),请使用V8,否则请使用原发动机。 现在我们对霍华德·希南特(搬家提案的主要作者)的 answer 在SO上:
编辑
:Howard添加了一些非常值得一提的评论。根据定义,移动语义的行为更像引用语义,因为“移动到”和“移动自”对象不是独立的,当修改(通过移动构造或移动指定)移动到的对象时,也会修改“移动自”对象。然而
移动语义何时发生并不重要,您不关心从对象移动
,它要么是纯右值(因此没有其他人引用原始值),要么当程序员明确表示“我不关心复制后原始值”(通过使用
移动语义和性能优化
正如提案中所述,人们从移动语义中获得的主要好处是性能提升。这里我举两个例子。 您可以看到的优化假设我们有一个构造成本很高的处理程序(不管是什么),我们希望将其存储到一个映射中以供将来使用。
这是move的典型用法,当然它假定
你看不到的优化
Howard Hinnant在他的
talk
移动语义的思想来自于优化
A.
更重要的是,移动还可以提升向量
图上显示的所有内容都在堆上,包括向量的数据缓冲区和每个元素字符串的数据缓冲区。有副本,
移动语义和资源管理在那篇著名的文章中 Rule of Zero ,作者写道:
我发现讨论移动语义和资源管理之间的相关性是一个很好的起点。 你可能知道,也可能不知道,RAII还有一个名字叫 范围绑定资源管理 (SBRM),在RAII对象的生存期因范围退出而结束的基本用例之后。还记得使用值语义的一个优点吗?安全我们只需查看对象的 storage duration ,99%的时候我们会在块范围内找到它,这使得它非常简单。指针和引用的情况变得更加复杂,现在我们不得不担心被引用或指向的对象是否已被释放。这很困难,更糟糕的是,这些对象通常存在于与其指针和引用不同的范围内。 很明显,为什么值语义与RAII相处得很好?RAII将资源的生命周期与对象的生命周期绑定在一起,通过值语义,您可以清楚地了解对象的生命周期。 但是,资源是关于身份的虽然值语义和RAII似乎是完美的匹配,但实际上并非如此。为什么?从根本上讲,因为资源是关于身份的,而值语义只关心值。你有一个打开的插座,你使用的就是这个插座;你有一个打开的文件,你使用的就是这个文件。在资源管理的上下文中,没有具有相同价值的东西。资源用唯一标识表示自身。 看到这里的矛盾了吗?在C++11之前,如果我们坚持使用值语义,就很难使用资源,因为它们无法复制,因此程序员想出了一些解决方法:
这些解决方案旨在解决唯一所有权和所有权转移问题,但都存在一些弊端。我不会在这里谈论它,因为它在互联网上无处不在。我想说的是,即使没有移动语义,也可以进行资源所有权管理,只是它需要更多的代码,而且往往容易出错。
与提案中的上述声明相比,我喜欢这样 answer 更多信息:
上面的引用很好地解释了移动语义对C++中的资源所有权管理意味着什么。资源自然应该是可移动的(我所说的“可移动”是指可转移的),但不可复制,现在在移动语义的帮助下(实际上在语言级别上有很多变化来支持它),有一种标准方法可以正确有效地做到这一点。 价值语义学的重生最后,我们可以讨论扩展的另一个方面(除了性能),即移动语义带来的价值语义。 通过以上讨论,我们了解了为什么值语义适合RAII模型,但同时又与资源管理不兼容。随着移动语义的出现,填补这一空白的必要材料终于准备好了。现在我们有了,聪明的指针!
不用说
第三点,如果你读过 零法则 ,你知道我在说什么。无需使用原始指针来管理资源,只需直接使用unique\u ptr或存储为成员变量,就可以了。在转移资源所有权时,隐式构造的move-ctor能够很好地完成这项工作。更妙的是,当前规范确保在最坏的情况下(即没有省略)返回语句中的命名值被视为右值。这意味着, 按值返回应该是unique\u ptr的默认选择 。
看见 here 以获取更详细的解释。事实上 当使用unique\u ptr作为函数参数时,按值传递仍然是最佳选择。 如果时间允许,我可能会写一篇关于它的文章。
除了智能指针,
总之,通过使用智能指针,值语义现在真正获得了与RAII的兼容性。其核心是移动语义。 总结到目前为止,我们已经讨论了很多概念,您可能会感到不知所措,但我想表达的要点很简单:
我自己也是这个话题的学习者,所以请随时指出你认为错误的地方,我真的很感激。 [1] :此处对象表示“ 一种具有地址、类型并能存储值的存储器 “,发件人 Andrzej's C++ blog 。 |
![]() |
Demaunt · 复制构造函数和移动语义之间的C++差异 7 年前 |
|
user9267359 · Rational类和移动语义不起作用 7 年前 |
![]() |
Remi.b · 保持原始对象的同时移动 7 年前 |
![]() |
Navie · 是否将prvalue移动到函数模板uref参数中? 7 年前 |
![]() |
Xirema · 如何正确编写运算符的R值重载 7 年前 |
|
tuuttuut · Numpy数组传递值 7 年前 |
![]() |
Xyten · 哪个构造函数将触发移动语义? 7 年前 |