为什么来自unique_ptr析构函数的异常终止程序?

时间:2016-06-13 11:11:51

标签: c++ c++11 unique-ptr

看看这段导致程序终止而不会捕获异常的代码。

#include <iostream>
#include <string>
#include <memory>
#include <stdexcept>
using namespace std;

struct test {
  ~test() noexcept(false) {
    throw runtime_error("-my-cool-exception-");
  }
};

int main()
{
  try {
    auto ptr = unique_ptr<test>(new test());
    //test t; // this is ok, without unique_ptr<test> it works fine.
  }
  catch(exception& e) {
    cout << "this is not called, the program is aborted";
    cout << e.what() << endl;
  }
  return 0;
}

这个问题与堆栈溢出问题不同:throwing exceptions out of destructor

不同之处在于,只有当我使用unique_ptr<test>时才会捕获到异常。

您可以在此处查看实时代码,编辑和编译http://cpp.sh/9sk5m

1 个答案:

答案 0 :(得分:13)

标准要求的。根据{{​​3}}:

  

要求:表达式get_deleter()(get())应该格式良好,具有明确定义的行为,不应抛出异常。 [注意:使用default_delete需要T为完整类型。 - 结束说明]

(强调我的)

实际上,析构函数本身是否具有noexcept(false)并不重要。它被禁止投入此案 - 最后一句话。

案例std::的情况通常如此 - 除非另有说明。根据{{​​3}}:

  

特别是,在以下情况下,效果未定义:   [...]   如果任何替换函数或处理函数或析构函数通过异常退出,除非在适用的必需行为中明确允许:段落

(强调我的)

注意:通常,可以使用noexcept(false)投掷析构函数。但是,这非常危险,因为如果堆栈因抛出异常而展开,将调用 std::terminate 。 根据{{​​3}}:

  

当控件从抛出异常的位置传递到处理程序时,析构函数由本节中指定的进程调用,称为堆栈展开。 如果通过堆栈展开直接调用的析构函数以异常退出,则调用std :: terminate([except.terminate])。 [注意:因此,析构函数通常应该捕获异常,而不是让它们从析构函数中传播出来。 - 结束说明]

(强调我的)

unique.ptr.single.dtor, 20.10.1.2.2.1