【问题标题】:Destruction of returned object返回对象的销毁
【发布时间】:2020-08-02 11:09:33
【问题描述】:

我正在阅读C++17 standard draft 中第 18 节异常处理的第 18.2 节构造函数和析构函数,其中有一个示例(第 384 页)。我试图了解在 try 块内返回的对象 A 的破坏发生在哪里,但找不到。所以我复制了这个例子,添加了一些打印结果,发现该对象的 dtor 从未被调用过。我在这里想念什么?有人请解释一下这里发生了什么。

#include <stdio.h>

struct A {
    int a_;
    explicit A(int a):a_(a) {
        printf("A(%d)'s ctor\n", a_);
    }
    ~A() {
        printf("A(%d)'s dtor\n", a_);
    }
};

struct Y { ~Y() noexcept(false) {
    printf("y's dtor\n");
    throw 0; } };

A f() {
    try {
        A a(23);
        Y y;
        A b(56);
        return A(100); // #1 who destructs this ??
    } catch (...) {
        printf("handling exception..\n");
    }
    printf("At line %d now..\n", __LINE__);
    return A(200); // #2
}

int main() {
    auto ret = f();
    printf("f returned A(%d) object\n", ret.a_);
    return 0;
}

以上代码输出如下:

A(23)'s ctor                                                                                                                                                  
A(56)'s ctor                                                                                                                                                  
A(100)'s ctor                                                                                                                                                 
A(56)'s dtor                                                                                                                                                  
y's dtor                                                                                                                                                      
A(23)'s dtor                                                                                                                                                  
handling exception..                                                                                                                                          
At line 34 now..                                                                                                                                              
A(200)'s ctor                                                                                                                                                 
f returned A(200) object                                                                                                                                      
A(200)'s dtor                                                                                                                                                 


...Program finished with exit code 0

【问题讨论】:

  • 你已经演示了为什么析构函数永远不能安全地抛出异常。
  • 您链接的文档是 C++17 后的草案(请参阅 N4659 了解 C++17 标准)
  • @ChrisDodd 不是,这个程序是为了定义明确的
  • 问题在于,在析构函数上允许任何异常的任何异常规范都无法安全地实现,因为在该帧完全销毁之前退出该帧时可能会引发异常。
  • @ChrisDodd 标准没有提到“框架”

标签: c++ exception return-value destructor throw


【解决方案1】:

通常,函数的调用者负责销毁函数的返回值,但具体细节是平台 C++ ABI 的一部分。

在大多数 ABI 上,调用一个返回非平凡值的函数(任何不能安全地放入一个或两个寄存器中的东西)是通过传递一个隐藏的额外参数来完成的,该参数指定应该在哪里构造返回值。调用者在其框架中为返回值分配空间,并传递一个指向该空间的指针。

【讨论】:

  • 如果我理解正确,调用者(此处为main)退出并且ret 被破坏,如A(200)'s dtor 行所示。在它之前的某个地方,我期待看到A(100)'s dtor 行,这让我感到困惑。可能,我需要了解您提到的在析构函数中引发异常的情况的细微差别和其他细节!
  • @VishalSahu 标准明确表示在A(100),构造了返回的类型 A 的对象。然后,局部变量 b 被销毁(8.7)。接下来,局部变量y被销毁,导致栈展开,导致返回的对象被销毁,接着是局部变量a的销毁
【解决方案2】:

根据C++17标准[except.ctor]/2:

如果在销毁 return 语句的临时变量或局部变量期间引发异常,则还会调用返回对象(如果有)的析构函数。对象按其构建完成的相反顺序被销毁。

然后有一个说明性示例(您对其稍作修改)来演示:在Y 析构函数抛出之后,接下来发生的事情是应该销毁A(100) 对象,因此您应该会看到一条销毁消息。

发布的输出表明存在编译器错误。这与报告为LLVM bug 12286gcc bug 33799 的问题非常相似。

后者在 GCC 10 中被标记为已修复(在 2007 年首次报告之后!)。但是,用10.0.1 20200418 (experimental)on Godbolt这个版本进行测试:即使33799 bug报告中的测试用例已经修复,这个问题中的代码仍然没有修复。我在该错误报告中添加了关于此示例的评论。

【讨论】:

  • §14.3,例子和问题很相似。
  • @TonyTannous 是的,我假设 OP 查看了该示例并添加了一些输出调试
  • 这似乎是标准中的一个坏问题——实现这一点需要在每个可能引发异常的对象的帧中产生重大开销(我想这还不错,因为没有真正的程序将永远声明一个析构函数 noexcept(false))
  • @ChrisDodd 如果编译器供应商故意违反标准,那么他们的错误跟踪器上应该有一个讨论线程(尽管搜索这些东西可能是一场噩梦)
猜你喜欢
  • 2013-10-24
  • 2016-02-15
  • 1970-01-01
  • 1970-01-01
  • 2014-09-24
  • 1970-01-01
  • 1970-01-01
  • 2017-01-11
  • 2013-03-21
相关资源
最近更新 更多