提问人:Nathan Doromal 提问时间:7/20/2023 最后编辑:DailyLearnerNathan Doromal 更新时间:7/21/2023 访问量:136
为什么删除复制或移动构造函数时,命名返回值优化的 C++ 编译失败?
Why does C++ compilation for named return value optimization fail when the copy or move constructor is deleted?
问:
我在 C++ 上的 C++ 上的 gcc 13.1 上尝试了以下操作11/17/20/23,但在删除移动或复制构造函数时无法编译。
如果未删除这些构造函数,则命名返回值优化将起作用,并且不会执行复制/移动。
有趣的是,如果我删除名称并直接返回 prvalue,那么普通返回值优化就会起作用。
谁能对此做出解释?
#include <memory>
#include <iostream>
struct Foo{
Foo(int v): a{v} { std::cout << "Create!\n"; }
~Foo() { std::cout << "Destruct!\n"; }
Foo(const Foo&)=delete;
Foo(Foo&&)=delete;
int a;
};
// I DON'T WORK!
Foo makeFoo() {
Foo foo{5};
return foo;
}
// I WORK!
//Foo makeFoo() {
// return Foo{5};
//}
int main() {
auto foo = makeFoo();
std::cout << "Hello world! " << foo.a << "\n";
}
答:
5赞
Paul Sanders
7/20/2023
#1
尽管从 C++17 开始,复制省略确实是强制性的,但在按值从函数返回命名对象时,您仍然必须提供移动构造函数是有原因的。
这是因为可以在无法编写 NVRO 的地方编写代码。下面是一个简单的示例:
std::string foo (bool b)
{
std::string s1 = "hello";
std::string s2 = "world";
return (b) ? s1 : s2;
}
现在,NVRO 的工作原理是为要在调用站点返回的对象分配内存,然后在 中构造对象时执行放置。new
foo
但是对于上面的代码,编译器无法做到这一点(无论如何在一般情况下都不是),因为它可能会返回两个可能的对象,并且它事先不知道它将是哪一个。因此,它被迫根据传入的内容从 或 移动构造返回的字符串。s1
s2
b
现在你可以争辩说,在编译器没有遇到这个问题的代码中,NVRO不应该需要一个可行的移动构造函数。但委员会显然认为,什么是允许的,什么是不允许的,那么就太令人困惑了,我同意他们的观点。让我们面对现实吧,对于编译器编写者来说,生活已经够艰难了。
评论