返回本地对象是否需要移动语义

Does returning a local object require move semantics?

本文关键字:移动 语义 是否 对象 返回      更新时间:2023-10-16

当按值返回本地对象时,C++编译器可以利用移动语义来优化不必要的副本(副本省略)
"可能优化"意味着,如果不满足适当的条件,则行为应返回到基于复制的默认按值返回语义
因此,据我所知,按值返回可复制对象总是有效的。

但是编译器(clang和gcc)似乎不同意我的解释,如下面的MWE所示。

class Foo {
public:
    Foo();
    Foo(const Foo&);
    Foo(Foo&&) = delete;
}
Foo f() { return Foo(); }  // error: call to explicitly deleted constructor of 'Foo'
Foo g() { Foo a; return a; }  // gcc complains, clang is fine
Foo x = g();  // error: call to explicitly deleted constructor of 'A'

Q1:按价值退货是否要求物品是可移动的
Q2:如果没有,是gcc和clang在我的MWE上表现不好,还是我错过了其他东西?

您只是满足了重载解析的预期行为:Foo()是一个右值,因此重载解析会发现构造函数Foo(Foo&&)是最佳匹配。由于该重载已被删除,因此您的程序格式不正确。此外,还有一个特殊的规则,即Foo a; return a;也将执行过载解析,就好像a首先是一个右值一样。(该规则基本上适用于返回语句符合副本省略条件的情况。)

这一切都按预期进行。是你删除了超载,所以你明确要求禁止这种建筑。

请注意,"真实"代码通常不会遇到这个障碍,因为一旦您声明了一个复制构造函数,您的类就不会有任何移动构造函数。但你特意说,"不,实际上我确实想要一个move构造函数,如果有人试图使用它,我希望它是一个错误"。

关于此:

Foo g() { Foo a; return a; }  // gcc complains, clang is fine

GCC是对的,这不应该编译,因为[class.copy]/32(强调矿):

当满足省略复制/移动操作的条件,但不满足异常声明的条件时,并且要复制的对象由左值指定,或者当return语句中的表达式为(可能带括号)id表达式,该表达式命名具有在正文中声明的自动存储持续时间的对象,或最里面的封闭函数或lambda表达式的参数声明子句,重载解析为副本选择构造函数首先执行,就好像对象是由右值指定的一样。如果第一个过载解析失败或未执行,或者如果所选的第一个参数的类型构造函数不是对对象类型的右值引用(可能是cv限定的),重载解析是再次执行,将对象视为左值。[注意:此两阶段过载解决方案必须执行,而不管是否会发生复制省略。如果省略,它将确定要调用的构造函数不执行,并且即使取消调用,所选构造函数也必须是可访问的--结束音符]

因此,实现应该选择move构造函数进行省略,因为它被删除了,所以程序格式不正确。