不,最佳做法是直接return t;
.
如果类T
有未删除的移动构造函数,并且 noticet
是一个return t
符合复制省略条件的局部变量,它就像移动构造返回的对象一样return std::move(t);
。但是return t;
仍然可以复制/移动省略,因此可以省略构造,而return std::move(t)
始终使用移动构造函数构造返回值。
如果类中的移动构造函数T
被删除但复制构造函数可用,return std::move(t);
则不会编译,但return t;
仍使用复制构造函数进行编译。与提到的@Kerrek不同,t
它没有绑定到右值引用。对于符合复制省略的返回值有一个两阶段的重载解决方案——先尝试移动,然后复制,移动和复制都可能被省略。
class T
{
public:
T () = default;
T (T&& t) = delete;
T (const T& t) = default;
};
T foo()
{
T t;
return t; // OK: copied, possibly elided
return std::move(t); // error: move constructor deleted
return static_cast<T&>(t); // OK: copied, never elided
}
如果return
表达式是左值并且不符合复制省略的条件(很可能您正在返回非局部变量或左值表达式)并且您仍然希望避免复制,那么std::move
这将很有用。但请记住,最佳实践是让复制省略成为可能。
class T
{
public:
T () = default;
T (T&& t) = default;
T (const T& t) = default;
};
T bar(bool k)
{
T a, b;
return k ? a : b; // lvalue expression, copied
return std::move(k ? a : b); // moved
if (k)
return a; // moved, and possibly elided
else
return b; // moved, and possibly elided
}
标准中的 12.8(32) 描述了该过程。
12.8 [class.copy]
32 当满足或将满足复制操作省略的条件时,除了源对象是函数参数的事实,并且要复制的对象由左值指定时,重载决策以选择复制的构造函数就像对象由右值指定一样首先执行。如果重载决议失败,或者如果所选构造函数的第一个参数的类型不是对对象类型的右值引用(可能是 cv 限定的),则再次执行重载决议,将对象视为左值。[注意:无论是否会发生复制省略,都必须执行此两阶段重载解析。它确定如果不执行省略则要调用的构造函数,并且即使调用被省略,所选构造函数也必须是可访问的。——尾注]