【问题标题】:Difference between C++03 throw() specifier C++11 noexceptC++03 throw() 说明符 C++11 noexcept 之间的区别
【发布时间】:2012-10-01 17:52:17
【问题描述】:

throw()noexcept 除了分别在运行时和编译时检查之外,还有什么区别吗?

This Wikipedia C++11 article 建议弃用 C++03 抛出说明符。
为什么会这样,noexcept 是否足以在编译时涵盖所有这些?

[注意:我检查了this questionthis article,但无法确定弃用的确切原因。]

【问题讨论】:

  • 根据 nice articlenoexcept 可能会导致运行时检查。它们之间的主要区别在于破坏noexcept 导致std::terminate 而破坏throw 导致std::unexpected。在这些情况下,堆栈展开行为也略有不同。
  • 没有任何“编译时”检查与某些异常规范是“运行时”检查其他。这只是 C++ 异常规范的反对者创造的神话。

标签: c++ exception c++11 throw noexcept


【解决方案1】:

std::unexpected() 在违反动态异常规范时由 C++ 运行时调用:从异常规范禁止此类异常的函数抛出异常。

std::unexpected()也可以直接从程序中调用。

在任何一种情况下,std::unexpected 都会调用当前安装的std::unexpected_handler。默认的std::unexpected_handler 调用std::terminate

【讨论】:

    【解决方案2】:

    noexcept 在编译时不会被检查。

    实现不应仅仅因为表达式在执行时抛出或可能抛出包含函数不允许的异常而拒绝表达式。

    当声明为noexceptthrow() 的函数尝试抛出异常时,唯一的区别是一个调用terminate,另一个调用unexpected,后一种异常处理方式已被有效弃用。

    【讨论】:

    • 但是如果虚函数有throw()/noexcept,编译时检查确保覆盖器也有。
    【解决方案3】:

    不推荐使用异常说明符,因为 exception specifiers are generally a terrible idea。添加noexcept 是因为它是异常说明符的一种相当有用的用法:知道函数何时不会 抛出异常。因此它变成了一个二元选择:会抛出的函数和不会抛出的函数。

    添加了noexcept 而不是仅仅删除除throw() 之外的所有抛出说明符,因为noexcept 更强大。 noexcept 可以有一个编译时解析为布尔值的参数。如果布尔值为真,则 noexcept 将保持不变。如果布尔值为 false,则 noexcept 不会粘住,函数可能会抛出。

    因此,您可以这样做:

    struct<typename T>
    {
      void CreateOtherClass() { T t{}; }
    };
    

    CreateOtherClass 会抛出异常吗?如果T 的默认构造函数可以,它可能会。我们怎么讲?像这样:

    struct<typename T>
    {
      void CreateOtherClass() noexcept(is_nothrow_default_constructible<T>::value) { T t{}; }
    };
    

    因此,CreateOtherClass() 将在给定类型的默认构造函数抛出时抛出。这解决了异常说明符的主要问题之一:它们无法向上传播调用堆栈。

    throw() 无法做到这一点。

    【讨论】:

    • +1 有用的答案,无论如何对我来说。仍在寻找说明我为什么要使用noexcept 的答案。我从来没有使用过throw() 说明符,并且正在尝试确定noexcept 是否真的提供了任何好处(除了编译器检查的文档)。
    • @NicolBolas 同意。但是如果 noexcept 是一个保证,编译器可以检查一个函数是否可以在析构函数中抛出。因此能够警告程序员一个函数是否为 noexcept。
    • @NicolBolas 运行时调用std::terminate。这是WAY WORSE!代码可以潜入具有标记为noexcept 的功能的版本中,并在运行时(即在客户站点)检测到违规行为。我的意思是编译器保证生成的代码一开始就不会抛出异常。
    • @NicolBolas:另一个值得注意的区别。如果一个函数被标记为throws(),那么如果抛出异常,堆栈必须被展开到该函数的范围(因此函数中的所有自动变量都被销毁),此时terminate()是调用(通过unexpected())。如果函数被标记为noexcept,那么如果抛出异常,则调用终止(堆栈的展开是实现定义的细节)。
    猜你喜欢
    • 2015-06-28
    • 2019-09-22
    • 1970-01-01
    • 1970-01-01
    • 2018-01-28
    • 2018-02-19
    • 1970-01-01
    • 2020-08-03
    相关资源
    最近更新 更多