返回 std::move(m_field) 还是返回 m_field?

raf*_*foo 5 c++ move-semantics

我读过其他一些文章,这些文章move是不必要的,甚至对 NRVO 等机制进行了去优化。但在大多数情况下,这是关于当地领域的。

\n\n

考虑这个例子:

\n\n
    \n
  • 类在计算后存储临时字段。
  • \n
  • 我想获取这个字段而不复制它(“消耗”它)。
  • \n
\n\n

std::move在这种情况下,使用on是否合适return?\n具体来说,这些代码段是否等效?\n第二个更“通用”,让用户决定是否应该移动结果,第一个更严格\xe2\x80\x94 任何调用都会消耗结果。

\n\n

选项1

\n\n
\n// Option 1\n\n#include <utility>\n\ntemplate<typename T>\nclass TemporaryResultHolder\n{\npublic:\n    void doTask() { result = T(); }\n\n    T getResult() { return std::move(result); }\n    // Or return T&& ?\n\nprivate:\n    T result;\n};\n\nint main() {\n    TemporaryResultHolder<int> holder;\n    holder.doTask();\n    int res = holder.getResult();\n\n    return 0;\n}\n
Run Code Online (Sandbox Code Playgroud)\n\n

选项2

\n\n
\n// Option 2\n\n#include <utility>\n\ntemplate<typename T>\nclass TemporaryResultHolder\n{\npublic:\n    void doTask() { result = T(); }\n\n    T getResult() { return result; }\n\nprivate:\n    T result;\n};\n\nint main() {\n    TemporaryResultHolder<int> holder;\n    holder.doTask();\n    int res = std::move(holder.getResult());\n\n    return 0;\n}\n
Run Code Online (Sandbox Code Playgroud)\n

Ast*_*ngs 5

是的,std::move(result)在这里是合适的,只要意图正是这样:从对象中“窃取”值并将其所有权传递给调用范围。

不过,我建议:

  1. 重命名该函数takeResult(),以显示它正在做什么。

  2. 或者使用 来限定函数本身&&,以增加安全性:那么只有当通过右值引用对象本身时才能使用此函数

T getResult() && { return std::move(result); }
// ...
holder.doTask();
int res = std::move(holder).getResult();
Run Code Online (Sandbox Code Playgroud)


eer*_*ika 4

如果你要返回一个带有自动存储的对象,那么你永远不应该返回 with std::move,因为前者在最好的情况下更快,在最坏的情况下相等。

我在其他一些帖子中读到,这是不必要的,甚至会去优化 NRVO 等机制。

这就是原因。然而,这不适用于问题中的函数,因为它不返回具有自动存储的对象,而是返回成员变量。NRVO 不能应用于成员变量,也不能隐式移动返回值。

因此,在您的示例中,return std::move(result);进行移动并return result;进行复制。如果函数的目的是从成员中移动,则应使用 move;如果函数的目的是复制成员,则应使用 copy。但请注意,从非右值限定的函数中移动成员是非常非常规的设计,我建议避免这样做,除非您有充分的理由这样做。另请注意,复制版本可以是 const 限定的,这将使其更普遍有用。

PS 对于int示例中使用的,这些区别是无关紧要的。移动与应对仅与非平凡的可复制/可移动类型相关。