return std::move(m_field) 还是 return m_field?
return std::move(m_field) or return m_field?
我在其他一些帖子中读到 move
是不必要的,甚至会取消优化 NRVO 等机制。但在大多数情况下,它是关于本地字段的。
考虑这个例子:
- A class 在计算后存储一个临时字段。
- 我想在不复制的情况下使用该字段("consuming")。
在这种情况下,在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" 对象的值并将其所有权传递给调用范围。
不过,我建议:
重命名函数 takeResult()
,以显示其作用。
或者用 &&
限定函数本身,以增加安全性:那么你只能在通过右值
[ 引用对象本身时使用此函数=25=]
T getResult() && { return std::move(result); }
// ...
holder.doTask();
int res = std::move(holder).getResult();
我在其他一些帖子中读到 move
是不必要的,甚至会取消优化 NRVO 等机制。但在大多数情况下,它是关于本地字段的。
考虑这个例子:
- A class 在计算后存储一个临时字段。
- 我想在不复制的情况下使用该字段("consuming")。
在这种情况下,在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" 对象的值并将其所有权传递给调用范围。
不过,我建议:
重命名函数
takeResult()
,以显示其作用。或者用
[ 引用对象本身时使用此函数=25=]&&
限定函数本身,以增加安全性:那么你只能在通过右值
T getResult() && { return std::move(result); }
// ...
holder.doTask();
int res = std::move(holder).getResult();