当前位置: 首页 > 知识库问答 >
问题:

throw()函数应该总是在exception上展开堆栈并允许捕获exception还是必须调用std::Terminate?

康秋月
2023-03-14

我感兴趣的是这是否是标准强制的,是否被某个编译器违反了。我的观察是:

>

  • Visual Studio 2015/2017(with/ehsc):throw()函数中的堆栈没有展开(不调用d-tors),但异常退出函数,并被try/catch捕获。没有调用STD::Terminate。
  • gcc(6.3.0)中,throw()函数中的堆栈被解除,但随后调用std::terminate(try/catch不能cought异常)。但是在7.0(当前HEAD)中,没有堆栈被释放,将立即调用std::Termiante。实际上,gcc 7.0甚至对此发出警告:warning:throw将始终调用terminate()[-wterminate]forNOExceptFunctionWithObj2()。它使throw()表现为noext(true)。

    clang,在我检查过的所有版本中,展开函数堆栈(调用对象d-tors),然后调用std::Terminate。

    #include <iostream>
    #include <string>
    #include <vector>
    
    struct TestDataWithoutNoexcept {
      TestDataWithoutNoexcept() {
        std::cout << __FUNCTION__ << "\n";
      }
      ~TestDataWithoutNoexcept() {
        std::cout << __FUNCTION__ << "\n";
      }
      TestDataWithoutNoexcept(TestDataWithoutNoexcept const & rhs) {
        std::cout << __FUNCTION__ << "\n";
      }
      TestDataWithoutNoexcept(TestDataWithoutNoexcept && rhs) {
        std::cout << __FUNCTION__ << "\n";
      }
      TestDataWithoutNoexcept& operator=(TestDataWithoutNoexcept const& rhs) {
        std::cout << __FUNCTION__ << "\n";
      }
    };
    
    void NoExceptFunctionWithObj1() noexcept {
      TestDataWithoutNoexcept test;
      throw std::runtime_error("NoExceptFunctionWithObj1 ex.");
    }
    
    void NoExceptFunctionWithObj2() throw() {
      TestDataWithoutNoexcept test;
      throw std::runtime_error("NoExceptFunctionWithObj2 ex.");
    }
    
    int main()
    {
      // Now lets see whether stack is being unwound when exception is thrown in noexcept versus throw() function.
      std::cout << "\n See how dtors are called in noexcept or throw() functions\n";
      try {
        //NoExceptFunctionWithObj1();
      }
      catch (std::runtime_error& ex) {
        std::cout << ex.what();
      }
    
      try {
        NoExceptFunctionWithObj2();
      }
      catch (std::runtime_error& ex) {
        std::cout << "\nShouldn't this be shown? : " << ex.what();
      }
    }
    
  • 共有1个答案

    段干华皓
    2023-03-14

    是,应调用std::Terminate。最新公布的标准草案称:

    15.4[例外规定],第12段

    如果异常规范的形式为throw()、noexcept或noexcept(constant-expression),其中constant-expression,其中constant-expression),则异常规范是非抛出的。

    当搜索一个处理程序(15.3)遇到一个函数的最外层块,该函数的noexcept-specification不允许出现异常(15.4)[...]std::Terminate()被调用(18.8.3)。在没有找到匹配处理程序的情况下,在调用std::terminate()之前是否展开堆栈是由实现定义的

    不调用std::terminate表示MSVC不兼容。

    关于堆栈的处理,在您的示例中,编译器执行它想要的关于是否展开堆栈的操作--这被指定为实现定义的。

     类似资料:
    • 问题内容: 我的第一个问题是- 构造函数应始终声明为公共吗? 如果我创建一个构造函数该怎么办。 我总是看到构造函数是隐式的。那么为什么构造函数有用呢?还是根本没有用。因为没有人可以调用它,或者永远也不能创建对象(由于构造函数)!这是我的第二个问题。 问题答案: 不, 构造函数 可以是,, 或(根本没有访问修饰符)。 做某事 并不意味着没有人可以访问它。这只是意味着该类之外的任何人都不能访问它。因此

    • 这对我来说很奇怪。继承自,后者继承自。 但是 还有,这有点企业设计的味道。不同的程序员或团队可以添加回调来处理该项,但它们应该相互隔离。这意味着,作为负责将这些回调相互隔离的程序员,我不应该依赖它们来确保错误不会溜走。捕捉应该是正确的,但这并不是因为会漏过。所以我更普遍的问题是:这里什么是好的模式?只是,我认为这是由于继承导致的语法错误?

    • 在 C 中实现回调函数时,我是否仍应使用 C 样式函数指针: 或者我应该使用std::函数:

    • 我使用mysql C++connector和这段(稍微简化了一点)代码。 连接器应该抛出SQL::SqlException,它派生自std::Exception并具有一些附加方法,如。 抛出的异常在第二个块中捕获,但可以成功转换为(并用作)。 更奇怪的是,不同可执行文件中的类似代码会像预期的那样捕获。它们之间的区别在于,第一个是在一个共享对象(.so)中,该对象加载了。

    • 问题内容: 我只知道非基元(对象)放在堆上,方法在堆栈上,但是基元变量呢? -更新 根据答案,我可以说堆可以有一个新的堆,并且给定对象可以堆吗?假设对象将具有基本变量和引用变量。 问题答案: 本地定义的基元将在堆栈中。但是,如果将原语定义为对象实例的一部分,则该原语将位于堆上。 关于更新: 对象没有自己的堆栈。在我的示例中,实际上是的每个实例的一部分。只要分配了HeapClass的实例(例如),就