如何解决MSVC 无法返回可以复制但不能移动的对象
在摆弄复制省略时,我遇到了这种奇怪的行为:
class Obj {
public:
Obj() = default;
Obj(Obj&&) = delete;
Obj(const Obj&) { std::cout << "copy" << std::endl; }
};
Obj f1() {
Obj o;
return o; // error C2280: move constructor is deleted
}
Obj f2() {
Obj o;
return Obj(o); // this however works fine
}
int main() {
Obj p = f1();
Obj q = f2();
return 0;
}
GCC 和 Clang 接受此代码并且能够在两种情况下使用复制省略。
在 f1()
中,MSVC 抱怨它无法返回 o
,因为 Obj
的移动构造函数被删除。但是,我希望它能够依靠复制构造函数。 这是 MSVC 中的错误还是这种期望的行为(我不明白)和 GCC/Clang 过于宽容?
如果我提供移动构造函数,MSVC 能够在编译为 Release 时省略移动。
有趣的是,MSVC 能够编译 f2()
。据我所知,这是由于返回构造函数调用的结果时强制复制省略。但是,如果我手动复制它,我只能按值返回 o
,这感觉违反直觉。
我知道这种情况可能与实际使用无关,因为可复制对象通常也是可移动的,但我对底层机制感兴趣。
以下是在线测试示例:https://godbolt.org/z/sznds7
解决方法
f1()
上没有错误是 clang 和 gcc 中的一个错误。 It is fixed in clang's tip-of-trunk.
f1()
不符合强制复制省略的条件。
已删除的函数参与重载决议。如果它们被选为最佳重载,则程序格式错误。在f1()
中,被删除的移动构造函数由重载决议选择。
在 f2()
、as of C++17,copy/move elision is guaranteed 中,因此未完成移动/复制构造函数的重载解析。在 C++11/14 中,f2()
也是一个错误(与 f1()
的错误相同),因为不保证复制/移动省略。
另请参阅此指南:Never delete the special move members,无可否认它是在 C++17 之前编写的。
,哦,我感到很惭愧,我刚刚意识到另一个答案是 Howard Hinnant 的,他的著作让我理解了我在这里痛苦地试图解释的内容,这有点荒谬......
强>因为复制和移动构造函数都声明了,所以它们都存在。 尤其是在这里,您小心地定义了自己的复制构造函数;如果没有它,它将被定义为删除(参见 this presentation 的第 28 页)。
删除方面只是定义的细节,但它们实际上都被声明然后有资格进行重载解析。
在 f1()
中,如果发生复制省略,则无需在复制和移动构造函数之间进行选择;这些都没有使用。
另一方面,如果没有发生复制省略,则必须选择最佳重载来构造结果;这里是移动构造函数,因为它存在(它被声明,见here),最后发现定义为删除,但为时已晚,已经做出选择。
在f2()
中,显式请求显式拷贝,那么拷贝构造函数是最好的选择。
令人困惑的是,当我们阅读 =delete
时,我们认为“这不能在重载决议中选择”,但这是错误的; =delete
仅在找到更好的匹配项为时已晚时才被视为之后重载决议。
版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。