return std::move(m_field) 还是 return m_field?

return std::move(m_field) or return m_field?

我在其他一些帖子中读到 move 是不必要的,甚至会取消优化 NRVO 等机制。但在大多数情况下,它是关于本地字段的。

考虑这个例子:

在这种情况下,在return上使用std::move合适吗? 具体来说,这些代码片段是否等价? 第二个更 "generic",让用户决定是否应该移动结果,第一个更严格——任何调用都会消耗结果。

选项 1


// Option 1

#include <utility>

template<typename T>
class TemporaryResultHolder
{
public:
    void doTask() { result = T(); }

    T getResult() { return std::move(result); }
    // Or return T&& ?

private:
    T result;
};

int main() {
    TemporaryResultHolder<int> holder;
    holder.doTask();
    int res = holder.getResult();

    return 0;
}

选项 2


// Option 2

#include <utility>

template<typename T>
class TemporaryResultHolder
{
public:
    void doTask() { result = T(); }

    T getResult() { return result; }

private:
    T result;
};

int main() {
    TemporaryResultHolder<int> holder;
    holder.doTask();
    int res = std::move(holder.getResult());

    return 0;
}

如果您return一个对象具有自动存储,那么您永远不应该return使用std::move,因为前者更快最好的情况,最坏的情况下相等。

I have read in some others posts that it's unecessarily and even deoptimize mecanisms like NRVO.

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

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

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

A "destructive" getResult 是非惯用的,所以你应该记录它。将 return 类型指定为 T&& 非常适合!这样,即使实现隐藏在 .cpp 文件中,阅读您的代码的开发人员也会理解这个特定的细节。

如果您 return 按值,则无论如何都会复制该值。即使你稍后 move 它,它已经被复制了,所以你的第二个选项浪费资源(当然假设 T 的实例很昂贵)。

另一种选择是 return引用 (const T&)。这里也没有复制,所以你也应该考虑一下。

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

不过,我建议:

  1. 重命名函数 takeResult(),以显示其作用。

  2. 或者用 && 限定函数本身,以增加安全性:那么你只能在通过右值

    [ 引用对象本身时使用此函数=25=]
T getResult() && { return std::move(result); }
// ...
holder.doTask();
int res = std::move(holder).getResult();