删除了所有自动生成的构造函数/运算符的类仍然可以从函数返回吗?

Class with all automatically-generated constructors/operators deleted can still be returned from a function?

提问人:Bernard 提问时间:7/20/2018 最后编辑:BarryBernard 更新时间:7/20/2018 访问量:1117

问:

最近,我遇到了这个答案,它描述了如何初始化一个非默认可构造的元素。我并不感到惊讶,因为这个答案显然没有进行任何默认构造。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,我们必须构造一个 .这意味着在函数中,我们正在从返回的 .这怎么可能?!foomake_foo()foomain()foofoo

C++ 返回值 C++17 删除函数

评论

2赞 Ron 7/20/2018
尝试使用 or 标志进行编译以观察不同的结果。-std=c++11-std=c++14

答:

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 初始化 ,因此我们只是忽略所有中间对象,并直接从实际初始值设定项初始化最外层对象。这完全等同于:foofoo

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&& fconst foo& f