为什么 C++ 中的强制 RVO 需要公共析构函数?

Fed*_*dor 37 c++ language-lawyer return-value-optimization

请考虑下面的简单示例,其中函数bar返回A具有私有析构函数的类对象,并且必须进行强制返回值优化(RVO):

class A { ~A() = default; };
A bar() { return {}; }
Run Code Online (Sandbox Code Playgroud)

该代码被 Clang 接受,但被 GCC 拒绝并出现错误:

error: 'constexpr A::~A()' is private within this context
    2 | A bar() { return {}; }
      |                   ^
Run Code Online (Sandbox Code Playgroud)

https://gcc.godbolt.org/z/q6c33absK

哪一个编译器就在这里?

Bri*_*ian 47

这是CWG 2426。析构函数可能在此上下文中被调用,因为即使在返回A对象初始化之后,该函数仍有可能无法成功完成:在return语句期间创建的任何临时变量,以及在范围内的自动局部变量,都必须被销毁,如果销毁抛出,那么作为堆栈展开的一部分,A对象将被销毁。此时编译器应该要求析构函数可以访问。

注 1:函数最外层作用域中局部变量的析构函数抛出的异常可以被函数 try 块捕获。

注2:返回对象销毁后,允许处理程序执行另一条return语句。标准中有这样的例子。

  • @LorahAttkins 仅当由于另一个异常而在堆栈展开期间完成。不适用于此处。 (6认同)
  • 在**此特定上下文**(OP 示例)中,*简单地证明* 从未调用过析构函数。其他例子就不那么好了。 (3认同)

Ded*_*tor 15

有很多简单的例子,比如在这个问题中,可以很容易地证明永远不会使用析构函数,但是使用了代码。

然而,决定这个问题可能会变得非常复杂,这是标准化的祸根。将它留在实现者的手中将因此使语言分裂,当他们为决定(不同的)极端情况而付出不同的努力时,会产生不兼容的子方言。

但这还不是结束,因为解决问题意味着解决停机问题,因此甚至不是棘手的,而是无法确定的

因此,像CWG 2426那样回避它不仅是为了理智(指定所有细节变得非常快变得笨拙),而且是唯一的选择,而不是在指定任意数量的任意选择的简单案例后反复无常地画线。