【问题标题】:about throw() in C++关于 C++ 中的 throw()
【发布时间】:2010-01-28 20:10:21
【问题描述】:

void MyFunction(int i) throw();

它只是告诉编译器该函数不会抛出任何异常。

它不能确保函数什么都不抛出,对吗?

那么 throw() 有什么用

它是多余的吗?为什么提出这个想法?

【问题讨论】:

  • 考虑阅读 Herb Sutter 关于该主题的文章:gotw.ca/publications/mill22.htm “道德 #1:永远不要编写异常规范。道德 #2:除非可能是空的,但如果我是你,我会避免即便如此。”
  • 我喜欢 emty 异常规范。但只要你保证它不会抛出任何东西。
  • “为什么提出这个想法”——因为当时它似乎是个好主意。 ;) C++ 有很多这样的“特性”,在今天被认为是坏主意,但在提出时看起来很棒。

标签: c++


【解决方案1】:

首先,当编译器工作正常时,它被强制执行——但在运行时,而不是编译时。具有空异常规范的函数不会抛出异常。如果发生某些事情会创建一个从它逃逸的异常,则会调用unexpected(),而后者(反过来)调用abort。您可以使用set_unexpected 更改调用的内容,但该函数所允许做的只是在中止程序之前添加额外的“东西”(例如清理)——它无法返回到原始执行路径。

也就是说,至少有一个主要编译器 (VC++) 解析异常规范,但不强制执行它们,尽管它可以使用空异常规范来稍微改进优化。在这种情况下,未遵循的异常规范可能/确实会导致未定义的行为,而不是必然中止程序。

【讨论】:

  • 我相信(非空)异常规范在最新的 C++0x 草案标准中也被标记为弃用,所以这一次,该标准似乎正朝着与 VC++ 保持一致的方向发展,而不是其他方式。 :)
  • @jalf:我在当前草案 (N3000) 中找不到任何表明这一点的内容。
  • 啊,看来我错了。有人支持弃用它,但尚未通过正式提案:herbsutter.wordpress.com/2009/11/11/… - 引用:“在会议快结束时,我们还讨论了弃用导出(正如我之前报道的那样)和 throw() 以外的异常规范- 什么都没有。似乎对弃用两者都有很大的支持,所以我们可能会在下次会议上看到一个具体的提议。”
【解决方案2】:

它不能确保函数什么都不抛出,对吗?

你快到了。这是一个异常规范。这意味着作为实施者,您向您的客户保证这段代码不会引发异常。但是,这不会阻止 MyFunction 中的某些函数抛出,如果您不处理它们,它们会冒泡并以您不希望的方式导致您/客户的程序。这甚至不意味着你不能在里面有一个throw 表达式。

最好避免这样的规范,除非你绝对确定你的代码永远不会抛出——除了非常基本的功能之外,这有点困难。参见标准swap、指针分配等。

它是多余的吗?为什么提出这个想法?

不完全是。如果使用得当,它可以帮助编译器进行优化。请参阅this 文章。 This 文章解释了不投掷井背后的历史。

进一步挖掘,我发现了来自 Boost 文档的 this 优秀文章。必读。阅读异常保证部分。

【讨论】:

  • AFAIK,throw() 的一致实现,虽然允许更好地优化 caller,但给 callee 带来了巨大的负担,这足以抵消调用者获得的任何好处;所以最后是净亏损。也就是说,你的第一个链接是一个专门描述 VC++ 的博客,它没有以一致的方式实现throw()(而不是在抛出时强制函数终止,它只是说它是 U.B. 如果它发生),这让它优化调用者而不为被调用者付出代价。您关于nothrow 的第二个链接与此无关。
  • 是的,说明没有投掷是有帮助的。第二个链接是关于历史的。有帮助吗?
  • @Pavel:throw() 的实现并没有带来那么大的负担。相当于:try { function_body(); } catch(...) { (*unexpected)(); }。大多数当前的编译器可以输入try 块而很少或没有开销,并且在这种情况下应该能够执行相同的操作。也就是说,真正的用途不是优化——只是如果没有至少一些保证不会抛出(任何东西,永远)的原语,几乎不可能使任何异常安全。
  • @Jerry:负担可能比人们预期的要大,例如许多编译器不会直接使用 try 块内联函数。
【解决方案3】:

正如您所说,它只是告诉编译器该函数不会引发任何异常。

当编译器预期可能的异常时,它通常必须以某种特定形式生成代码,这会降低效率。它还可能必须生成一些额外的“家庭”代码,以便在抛出异常时以及是否抛出异常时进行处理。

当你告诉编译器这个函数从不抛出任何东西时,它会让编译器更容易识别所有这些额外的异常相关开销完全没有必要的情况,从而帮助编译器生成更高效的代码。

请注意,如果在运行时您实际上尝试从使用throw() 规范声明的函数中抛出一些东西,则不允许异常离开该函数。相反,将调用一个所谓的意外异常处理程序,默认情况下它将终止程序。因此,从这个意义上说,实际上强制 throw() 函数不会抛出任何东西。

附:由于异常规范主要影响程序的运行时行为,因此通常它们可能没有任何编译时影响。然而,这个特定的异常规范 - 空的 throw() - 实际上在 编译时 被一些编译器识别,并且确实导致生成更高效的代码。有些人(包括我)认为空异常规范是唯一真正值得在实际代码中使用的规范。

【讨论】:

  • 讽刺的是,即使你声明了throw(),编译器也必须生成异常代码来执行你所说的运行时执行。
  • @Mark Ransom:我说的是在throw() 函数的外部 生成高效代码。另一方面,执行代码是函数的内部问题。
【解决方案4】:

说实话,现实世界中的异常规范并没有像原始设计者所设想的那样有用。此外,C++ 运行时检查异常规范和 Java 编译时检查异常规范之间的差异也造成了很多问题。

目前异常规范的例外规范有:

  • 不要使用它们。
  • 除非它们是空表格。
    • 确保如果您使用空表单保证您实际上捕获所有异常。

主要问题是,如果您有一个带有 throw 规范的方法。然后它使用下面的更改,现在抛出新类型的异常,代码没有警告或问题。但是在运行时,如果发生异常(不在 throw 规范中的新异常),那么您的代码将终止()。对我来说,终止是最后的手段,绝不应该在格式良好的程序中发生

【讨论】:

    猜你喜欢
    • 2012-06-15
    • 1970-01-01
    • 2010-12-22
    • 1970-01-01
    • 1970-01-01
    • 2015-10-28
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多