考虑支持默认移动语义的类型T. 还要考虑以下功能:
T f() {
T t;
return t;
}
T o = f();
Run Code Online (Sandbox Code Playgroud)
在旧的C++ 03中,一些非最佳编译器可能会调用两次复制构造函数,一个用于"返回对象",另一个用于o.
在c ++ 11中,由于tinside f()是左值,那些编译器可能像以前一样调用复制构造函数,然后调用o的移动构造函数.
是否正确说明避免第一次"额外复制"的唯一方法是t在返回时移动?
T f() {
T t;
return std::move(t);
}
Run Code Online (Sandbox Code Playgroud)
Ker*_* SB 48
号每当在一个局部变量return声明可享有复制省略,它结合一个右值参考,因此return t;是相同的return std::move(t);在例如相对于该构造有资格.
但请注意,这会return std::move(t); 阻止编译器执行复制省略return t; 没有,因此后者是首选的风格.[感谢@Johannes的修正.]如果发生复制省略,是否使用移动构造的问题变得没有实际意义.
见标准中的12.8(31,32).
还要注意,如果T有一个可访问的副本 - 但是删除了一个移动构造函数,那么return t;就不会编译,因为必须首先考虑移动构造函数; 你必须说一些return static_cast<T&>(t);让它起作用的东西:
T f()
{
T t;
return t; // most likely elided entirely
return std::move(t); // uses T::T(T &&) if defined; error if deleted or inaccessible
return static_cast<T&>(t) // uses T::T(T const &)
}
Run Code Online (Sandbox Code Playgroud)
小智 14
不,最佳做法是直接的return t;.
如果类T没有删除移动构造函数,并且notice t是一个return t有资格进行复制省略的局部变量,那么它会移动构造返回的对象return std::move(t);.但是return t;仍然有资格复制/移动elision,因此可以省略return std::move(t)构造,同时始终使用move构造函数构造返回值.
如果类中的移动构造函数T被删除但复制构造函数可用,return std::move(t);则不会编译,同时return t;仍使用复制构造函数进行编译.与@Kerrek提到的不同,t不受rvalue引用的约束.对于符合复制条件的返回值,有一个两阶段的重载分辨率 - 尝试先移动,然后复制,移动和复制都可能被省略.
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
}
Run Code Online (Sandbox Code Playgroud)
如果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
}
Run Code Online (Sandbox Code Playgroud)
标准中的12.8(32)描述了该过程.
12.8 [class.copy]
32当满足或将满足复制操作的省略标准时,除了源对象是函数参数这一事实,并且要复制的对象由左值指定,重载决策选择复制的构造函数首先执行,好像对象是由右值指定的.如果重载决策失败,或者所选构造函数的第一个参数的类型不是对象类型的rvalue引用(可能是cv-qualified),则再次执行重载决策,将对象视为左值.[注意:无论是否发生复制省略,都必须执行此两阶段重载决策.如果未执行elision,它将确定要调用的构造函数,并且即使调用被省略,也必须可以访问所选的构造函数. - 尾注]