3

Effective Modern C++ 中的第 29 项,Scott Meyers 列出了移动语义不会提高代码性能的三个场景,

[…] 移动语义对你没有好处:

  • 无移动操作:要移动的对象无法提供移动操作 […]
  • 移动不快: […] 移动操作不比其复制操作快。
  • 移动不可用:上下文 […] 需要一个不发出异常的移动操作,但该操作未声明noexcept

前面几页都解释清楚了,然后再加一个

[…] 移动语义没有提供效率增益的另一种情况:

  • 源对象是左值:除了极少数例外(参见条款 25),只有右值可以用作移动操作的源。

(第 25 项的标题为Use std::moveon rvalue references 和std::forwardon Universal references,但我看不出它与交叉引用它的项目符号点有何关系。)

在此之后,文本基本上回到了对该项目的总结,没有进一步提及第四个要点。

那个要点是指什么?

据我了解移动语义,即使我想从左值移动,比如说x,我仍然需要通过std::move(x)(或等效的static_cast)将它转换为右值,所以我在技术上仍然从右值移动(特别是 xvalue在这种情况下),而不是左值。

所以我很想说左值不能是移动操作的源对象。

我对这个话题缺少什么?

4

2 回答 2

1

术语左值是指以某种方式“命名”的值,即具有多个引用的实体。移动语义并不真正适用于它们,因为您不应该“窃取”可能在其他地方引用的某些东西的表示。也就是说,如果源对象是一个左值,你就永远不会移动!因此,移动构造在这里没有提供任何好处。事实上,左值不会自愿绑定到右值引用——你需要强制绑定,例如,通过使用std::move().

从本质上讲,您的观点是完全正确的:左值不能成为移动操作的来源 - 因此移动操作在涉及左值时不会提供好处。

于 2020-12-24T19:48:00.350 回答
-1

例如:您有一个带有移动构造函数的类 T。您有一个返回 T 类型对象的函数,并且您尝试通过返回 r 值来使其更快。现在如果你开始

T x;
x.a = ...;
x.b = ...;
x.c = ...;
return x;

然后将构造一个对象 x,通过 return 语句创建一个新的未命名对象,然后破坏 x,然后移动返回值。最终调用者将为移动的结果调用析构函数。所以你有两个构造函数,两个析构函数,没有节省。

如果你从

T x(a, b, c);
return x;

那么你有同样的问题,两个构造函数和析构函数,没有节省。要实际保存任何内容,您需要编写

return T(a, b, c);

或返回另一个返回对象的函数的返回值。

于 2020-12-24T21:22:53.420 回答