提问人:Bernard 提问时间:7/20/2018 最后编辑:BarryBernard 更新时间:7/20/2018 访问量:1117
删除了所有自动生成的构造函数/运算符的类仍然可以从函数返回吗?
Class with all automatically-generated constructors/operators deleted can still be returned from a function?
问:
最近,我遇到了这个答案,它描述了如何初始化一个非默认可构造的元素。我并不感到惊讶,因为这个答案显然没有进行任何默认构造。std::array
相反,它使用聚合初始化构造一个临时变量,然后在函数返回时移动(如果移动构造函数可用)或复制到命名变量中。因此,我们只需要移动构造函数或复制构造函数可用。std::array
或者我是这么想的......
然后出现了一段让我感到困惑的代码:
struct foo {
int x;
foo(int x) : x(x) {}
foo() = delete;
foo(const foo&) = delete;
foo& operator=(const foo&) = delete;
foo(foo&&) = delete;
foo& operator=(foo&&) = delete;
};
foo make_foo(int x) {
return foo(x);
}
int main() {
foo f = make_foo(1);
foo g(make_foo(2));
}
所有五个特殊成员构造函数/运算符都被显式删除了,所以现在我应该不能从返回值构造我的对象,对吗?
错。
令我惊讶的是,这是在 gcc(使用 C++17)中编译的!
为什么要编译这个?显然,要从函数中返回 a,我们必须构造一个 .这意味着在函数中,我们正在从返回的 .这怎么可能?!foo
make_foo()
foo
main()
foo
foo
答:
29赞
Barry
7/20/2018
#1
欢迎来到保证复制省略的奇妙世界(C++ 17 的新功能。另请参阅此问题)。
foo make_foo(int x) {
return foo(x);
}
int main() {
foo f = make_foo(1);
foo g(make_foo(2));
}
在所有这些情况下,您都是从 prvalue 初始化 ,因此我们只是忽略所有中间对象,并直接从实际初始值设定项初始化最外层对象。这完全等同于:foo
foo
foo f(1);
foo g(2);
我们在这里甚至不考虑移动构造函数 - 因此它们被删除的事实并不重要。具体规则是 [dcl.init]/17.6.1 - 只有在这一点之后,我们才会考虑构造函数并执行重载解析。
0赞
Jarod42
7/20/2018
#2
请注意,在 C++ 17 之前(在保证复制省略之前),您可能已经返回了带有 braced-init-lists 的对象:
foo make_foo(int x) {
return {x}; // Require non explicit foo(int).
// Doesn't copy/move.
}
但用法会有所不同:
foo&& f = make_foo(1);
foo&& g(make_foo(2));
评论
0赞
Bernard
7/20/2018
延长的寿命是否与?foo&& f
const foo& f
评论
-std=c++11
-std=c++14