当前标准是否保证在完整对象的初始化抛出异常时调用子对象的析构函数

Does the current standard guarantee to invoke the destructor for subobject when the initialization of the complete object throws an exception

except.ctor 部分,规则规定:

  1. As control passes from the point where an exception is thrown to a handler, objects with automatic storage duration are destroyed by a process, specified in this subclause, called stack unwinding.
  2. [...]
  3. If the initialization or destruction of an object other than by delegating constructor is terminated by an exception, the destructor is invoked for each of the object's direct subobjects and, for a complete object, virtual base class subobjects, whose initialization has completed ([dcl.init]) and whose destructor has not yet begun execution, except that in the case of destruction, the variant members of a union-like class are not destroyed. [ Note: If such an object has a reference member that extends the lifetime of a temporary object, this ends the lifetime of the reference member, so the lifetime of the temporary object is effectively not extended. — end note ] The subobjects are destroyed in the reverse order of the completion of their construction. Such destruction is sequenced before entering a handler of the function-try-block of the constructor or destructor, if any.

根据项目符号 3,哪些状态也可以视为堆栈展开过程的一部分。由于第一条规则听起来像是堆栈展开的调用仅适用于具有 自动存储持续时间 的对象。堆栈展开调用不是针对具有动态存储持续时间或其他持续时间的对象吗?

#include <iostream>
struct A{
  ~A(){
    std::cout<<"invoke\n";
  }
};
struct B{
 B(){
   throw 0;
 }
  A a;
};
int main(){
   try{  
      auto ptr = new B{};
    }catch(int){
   }
}

很明显,子对象a在B类型的完整对象中占用了动态存储期的存储空间。在我看来,A的析构函数应该被调用,因为它已经在B的初始化中完全构造出来了。GCC和Clang都同意that。它是这些编译器的扩展吗?如何解读?

通过[except.ctor]/3 的简单措辞,它适用于具有任何存储持续时间的对象,只要它们是构造因异常而终止的对象的子对象。这个应该没有争议。

但是,[except.ctor] 的措辞随着时间的推移发生了变化,这似乎造成了 OP 注意到的一些问题。背景是以前的写法和现在的写法很像,“stack unwinding”只是指自动对象的销毁,有人注意到这造成了不一致,当没有找到匹配的处理程序时,它不是保证是否发生堆栈展开,但它 保证发生子对象破坏(因为文本中没有任何内容表明如果找不到处理程序它可能不会发生)。这是 CWG 1774. CWG agreed that this inconsistency was undesirable (and perhaps it was unintentional, though that page doesn't say). So the wording was changed so that subobject destruction would be an aspect of "stack unwinding", and thus covered under [except.handle]/9, instead of being a separate process. But then later, to resolve a different DR 与异常无关,旧的措辞被添加回来,我 90% 确定这是无意的。 CWG2256 的决议的目的只是为了避免歧视普通可破坏的对象,而不是真正改变异常处理的任何内容。

因此,当前的措辞是有缺陷的,标准仍应理解为“堆栈展开”包括子对象销毁(,CWG 1774 的决议成立)。您甚至可以通过编辑方式修复此问题(,针对标准来源提交拉取请求)。