【问题标题】:noexcept and reliability guaranteesnoexcept 和可靠性保证
【发布时间】:2014-02-17 13:45:09
【问题描述】:

最近我试图在noexcept 异常规范中回答我认为是simple question 的问题。结果发现自己对noexcept的基本理解是错误的。

在阅读the current draft standard 以纠正我的误解时,我发现自己问了一些关于noexcept 的问题,而here 没有得到回答。

  1. 是否应该将noexcept 视为一种安全保证,即调用该函数时不仅不会抛出状态,而且不会破坏状态?
  2. 假设 (1.) 为假:使用 noexcept 作为可移植的 FailFast 来终止应用程序而不进行清理以防止损坏保存状态是否正确?

对 (2.) 的澄清: 目的只是为了防止从noexcept 进一步向上堆栈调用析构函数,而不是防止在其中展开。这是基于以下假设:这是一个完美的 RAII 环境,堆栈上的析构函数可以将全局状态刷新到持久性,从而破坏它。

不执行展开的示例:

#include <iostream>
#include <exception>

namespace{
   struct foo{
       void change_state() noexcept
       {
          // change state and fail
          throw std::exception();
       }
       ~foo(){
          std::cout << "Destructor called, saved state corrupted!" <<std::endl;
       }
    };
}


int main(){
    ::std::set_terminate([](){
        std::cout<< "Terminate called" <<std::endl;
    });

    foo f;
    f.change_state();
    return 0;
}

Working example of noexcept

【问题讨论】:

  • 据我所知noexcept :即使这件事抛出我也不想知道。所以它并不真正适用于“安全”或“正确性”,它通过防止抛出异常来工作,这样你的程序就不会因此而结束。
  • 如果异常到达标记为noexcept 的函数的最外层块,则调用std::terminate。它根本不抑制异常。它提供了一个障碍,可能不会引发异常
  • 我之前写的是关于runtimenoexcept 在编译时工作,所以它是给编译器的一个提示,编译器会评估你的程序,如果有机会抛出的东西,你有机会在 runtime 之前纠正这个问题,因为 noexcept 运算符将在 compile time 失败。它在某些方面像type traits 一样工作,你定义一个需要评估为true 的属性,如果不是,编译器会警告你,如果测试通过你知道在 runtime 你将拥有您在traits 条件中定义的类型。 noexcept 也是如此。
  • 我认为没有理由相信 1)(函数总是会破坏状态,即使没有例外),但我会说 2)是正确的。
  • @user2485710 我认为您无法理解noexcept 请阅读上述问题中的链接标准。这绝对不是noexcept 所做的。拥有一个标记为 noexcept 且其中包含 throw 语句的函数是完全合法的,甚至没有 catch 表示抛出。然而,结果将是调用std::terminate

标签: c++ language-lawyer noexcept


【解决方案1】:
  1. 是的。一个函数不应该破坏状态、周期,不管它是否抛出。它可能想要抛出异常这一事实无关紧要。 [当然,不能保证它不会使程序崩溃。不过,这种行为可能应该被记录下来。 ;-)]
  2. 亩。如果一个函数希望通过noexcept 抛出,它不应该打扰抛出;它应该只调用std::terminate(或std::abort)。

【讨论】:

  • 我理解你的意思,这样的失败应该是明确的,而不是依赖于noexcept 的行为。我想当标记为noexcept 的函数调用一个抛出的函数时,我正在研究理论。你捕捉到异常然后调用std::terminate
  • Eh...如果外部函数捕获和处理异常有用,那么它当然应该这样做。之后,它可能应该调用terminate 来证明它不需要维护类不变量。 throw 的意思之一是“但别担心,我们没事”。话虽如此,如果throw 距离noexcept 有一英寸的屏幕空间,那没关系。
【解决方案2】:

noexcept 是合同保证,很像assert 无法关闭。如果一个函数标记为noexcept 试图通过异常退出,后置条件已 违反了,你不能指望任何东西;该计划将是 终止。因此,1 为假。

关于2,难度更大。实施可能 展开堆栈,直到它尝试离开函数 noexcept;它可能不是 100% 清楚(至少对我来说) 不要进一步展开堆栈。如果我想终止 应用程序没有清理,然后我会打电话给abort(),这是 保证立即退出,无需任何进一步操作。它 还准确地告诉读者正在发生的事情,而不是 依靠不同功能的一些次要效果。

【讨论】:

  • 所以在这种特殊情况下,您可以使用catch 包装任何不安全(非noexcept)的调用?然后致电std::abort 以防止进一步展开? (我假设在 noexcept 函数中展开不会损坏保存状态)
  • @Mgetz 通常,我不会拨打任何不安全的电话:-)。否则,我只会在我想吞下它们时包装它们(例如,通常是析构函数,可以在堆栈展开期间调用)。
  • 看来我的问题并不清楚......目的只是为了防止析构函数调用比noexcept更进一步。但是,如果我理解您...您的意思是,在全局保存状态可能已损坏的情况下,最好在noexcept 上下文中执行操作,然后在需要时仅使用abort
  • @Mgetz 当您检测到错误时,最好立即清除,尽可能少做。是否值得额外努力立即检测错误,或者仅在您离开该功能时检测,是另一个问题。大多数时候,所涉及的功能应该足够小以至于无关紧要,不值得额外的努力。
猜你喜欢
  • 2019-12-02
  • 1970-01-01
  • 1970-01-01
  • 2023-04-02
  • 2014-12-25
  • 1970-01-01
  • 1970-01-01
  • 2020-03-21
  • 2013-01-27
相关资源
最近更新 更多