我应该在 C++ 中使用异常说明符吗?
在 C++ 中,您可以使用异常说明符指定函数可以抛出异常,也可以不抛出异常。 例如:
void foo() throw(); // guaranteed not to throw an exception
void bar() throw(int); // may throw an exception of type int
void baz() throw(...); // may throw an exception of some unspecified type
我对实际使用它们持怀疑态度,因为以下原因:
- 编译器并没有真正以任何严格的方式强制执行异常说明符,因此好处并不大。 理想情况下,您希望得到一个编译错误。
- 如果一个函数违反了异常说明符,我认为标准行为是终止程序。
- 在 VS.Net 中,它将 throw(X) 视为 throw(...),因此对标准的遵守并不强。
您认为应该使用异常说明符吗?
请回答“是”或“否”,并提供一些理由来证明您的答案。
In C++, you can specify that a function may or may not throw an exception by using an exception specifier. For example:
void foo() throw(); // guaranteed not to throw an exception
void bar() throw(int); // may throw an exception of type int
void baz() throw(...); // may throw an exception of some unspecified type
I'm doubtful about actually using them because of the following:
- The compiler doesn't really enforce exception specifiers in any rigorous way, so the benefits are not great. Ideally, you would like to get a compile error.
- If a function violates an exception specifier, I think the standard behaviour is to terminate the program.
- In VS.Net, it treats throw(X) as throw(...), so adherence to the standard is not strong.
Do you think exception specifiers should be used?
Please answer with "yes" or "no" and provide some reasons to justify your answer.
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(14)
异常规范 = 垃圾,问问任何 30 岁以上的 Java 开发人员
Exception specifications = rubbish, ask any Java developer over the age of 30
来自文章:
http://www.boost.org/community/exception_safety.html
事实上,我可以想出使模板类异常安全的方法。 除非您无法控制所有子类,否则您可能会遇到问题。 为此,可以在类中创建 typedef,定义各种模板类引发的异常。 我认为问题总是事后附加它,而不是从一开始就设计它,我认为这种开销才是真正的障碍。
From the article:
http://www.boost.org/community/exception_safety.html
And indeed I can think of ways to make template classes exception safe. Unless you don't have control over all the sub-classes then you may have a problem anyway. To do this one could create typedefs in your classes that define the exceptions thrown by various template classes. This think the problem is as always tacking it on afterwards as opposed to designing it in from the start, and I think it's this overhead that's the real hurdle.
它们对于单元测试很有用,这样在编写测试时您就知道函数失败时会抛出什么,但编译器中没有强制执行它们。 我认为它们是 C++ 中不必要的额外代码。 无论您选择哪一个,您都应该确保在整个项目和团队成员中遵循相同的编码标准,以便您的代码保持可读性。
They can be useful for unit testing so that when writing tests you know what to expect the function to throw when it fails, but there is no enforcement surrounding them in the compiler. I think that they are extra code that is not necessary in C++. Which ever you choose all that you should be sure of is that you follow the same coding standard across the project and the team members so that your code remains readable.
一般来说我不会使用异常说明符。 然而,如果任何其他异常来自有问题的函数,程序肯定无法纠正,那么它可能会很有用。 在所有情况下,请确保清楚地记录该函数可能出现的异常情况。
是的,从带有异常说明符的函数抛出的非指定异常的预期行为是调用terminate()。
我还要指出,Scott Meyers 在《更有效的 C++》中讨论了这个主题。 他的《Effective C++》和《More Effect C++》是强烈推荐的书籍。
Generally I would not use exception specifiers. However, in cases where if any other exception were to come from the function in question that the program would definitively be unable to correct, then it can be useful. In all cases, make sure to document clearly what exceptions could be expected from that function.
Yes, the expected behavior of a non-specified exception being thrown from a function with exception specifiers is to call terminate().
I will also note that Scott Meyers addresses this subject in More Effective C++. His Effective C++ and More Effective C++ are highly recommended books.
如果编译器知道函数永远不会抛出异常(或者至少承诺永远不会抛出异常),“throw()”规范允许编译器在进行代码流分析时执行一些优化。 Larry Osterman 在此简要讨论了这一点:
http://blogs。 msdn.com/larryosterman/archive/2006/03/22/558390.aspx
A "throw()" specification allows the compiler to perform some optimisations when doing code flow analysis if it know that function will never throw an exception (or at least promises to never throw an exception). Larry Osterman talks about this briefly here:
http://blogs.msdn.com/larryosterman/archive/2006/03/22/558390.aspx
避免 C++ 中的异常规范。 您在问题中给出的原因是一个很好的开始。
请参阅 Herb Sutter 的“异常规范的实用视角”。
Avoid exception specifications in C++. The reasons you give in your question are a pretty good start for why.
See Herb Sutter's "A Pragmatic Look at Exception Specifications".
我认为标准的 except 约定(对于 C++)
异常说明符是 C++ 标准中的一项实验,但大部分都失败了。
例外情况是 no throw 说明符很有用,但您还应该在内部添加适当的 try catch 块,以确保代码与说明符匹配。 赫伯·萨特(Herb Sutter)有一个关于这个主题的页面。 Gotch 82
另外,我认为值得描述一下例外保证。
这些基本上是关于对象的状态如何受到该对象上方法转义的异常影响的文档。 不幸的是,编译器没有强制执行或以其他方式提及它们。
Boost 和例外
例外保证
无保证:
基本保证:
强有力的保证:(又名交易保证)
无投掷保证:
I think the standardly except convention (for C++)
Exception specifiers were an experiment in the C++ standard that mostly failed.
The exception being that the no throw specifier is useful but you should also add the appropriate try catch block internally to make sure the code matches the specifier. Herb Sutter has a page on the subject. Gotch 82
In a addition I think it is worth describing Exception Guarantees.
These are basically documentation on how the state of an object is affected by exceptions escaping a method on that object. Unfortunately they are not enforced or otherwise mentioned by the compiler.
Boost and Exceptions
Exception Guarantees
No Guarantee:
Basic Guarantee:
Strong Guarantee: (aka Transactional Guarantee)
No Throw Guarantee:
当您违反异常规范时,gcc 将发出警告。 我所做的是使用宏仅在“lint”模式编译中使用异常规范,明确进行检查以确保异常与我的文档一致。
gcc will emit warnings when you violate exception specifications. What I do is to use macros to use the exception specifications only in a "lint" mode compile expressly for checking to make sure the exceptions agree with my documentation.
唯一有用的异常说明符是“throw()”,如“不抛出”。
The only useful exception specifier is "throw()", as in "doesn't throw".
异常规范在 C++ 中并不是非常有用的工具。 然而,如果与 std::unexpected 结合使用,它们会有很好的用途。
我在一些项目中所做的是带有异常规范的代码,然后使用一个函数调用 set_unexpected() ,该函数将抛出我自己设计的特殊异常。 此异常在构造时会获取回溯(以特定于平台的方式),并派生自 std::bad_exception (以允许在需要时传播它)。 如果它像通常那样导致终止()调用,则由what()打印回溯(以及导致它的原始异常;不难找到),因此我可以获得我的合同所在位置的信息违反了,例如抛出了意外的库异常。
如果我这样做,我永远不会允许传播库异常(除了 std 异常)并从 std::exception 派生所有异常。 如果一个库决定抛出,我将捕获并转换为我自己的层次结构,从而允许我始终控制代码。 出于显而易见的原因,调用依赖函数的模板化函数应避免异常规范; 但无论如何,很少有带有库代码的模板化函数接口(并且很少有库真正以有用的方式使用模板)。
Exception specifications are not wonderfully useful tools in C++. However, there /is/ a good use for them, if combined with std::unexpected.
What I do in some projects is code with exception specifications, and then call set_unexpected() with a function that will throw a special exception of my own design. This exception, upon construction, gets a backtrace (in a platform-specific manner) and is derived from std::bad_exception (to allow it to be propagated if desired). If it causes a terminate() call, as it usually does, the backtrace is printed by what() (as well as the original exception that caused it; not to hard to find that) and so I get information of where my contract was violated, such as what unexpected library exception was thrown.
If I do this, I never allow propagation of library exceptions (except std ones) and derive all my exceptions from std::exception. If a library decides to throw, I will catch and convert into my own hierarchy, allowing for me to always control the code. Templated functions that call dependent functions should avoid exception specifications for obvious reasons; but it's rare to have a templated function interface with library code anyway (and few libraries really use templates in a useful manner).
如果您编写的代码将被那些宁愿查看函数声明而不是其周围的任何注释的人使用,那么规范将告诉他们可能想要捕获哪些异常。
否则,我认为使用除了
throw()
之外的任何东西来指示它不会抛出任何异常都没有特别有用。If you're writing code that will be used by people that would rather look at the function declaration than any comments around it, then a specification will tell them which exceptions they might want to catch.
Otherwise I don't find it particularly useful to use anything but
throw()
to indicate that it doesn't throw any exceptions.是的,如果您喜欢内部文档。 或者也许编写一个其他人将使用的库,以便他们无需查阅文档就可以知道发生了什么。 抛出或不抛出可以被认为是 API 的一部分,几乎就像返回值一样。
我同意,它们对于在编译器中强制执行 Java 风格的正确性并不是很有用,但总比没有或随意的注释好。
Yes, if you're into internal documentation. Or maybe writing a libary that others will use, so that they can tell what happens without consulting the documentation. Throwing or not throwing can be considered part of the API, almost like the return value.
I agree, they are not really useful for enforcing correctness Java style in the compiler, but it's better than nothing or haphazard comments.
不会。如果您使用它们并且抛出了您未指定的异常(无论是由您的代码还是由您的代码调用的代码),那么默认行为是立即终止您的程序。
另外,我相信它们的使用在 C++0x 标准的当前草案中已被弃用。
No. If you use them and an exception is thrown that you did not specify, either by your code or code called by your code, then the default behavior is to promptly terminate your program.
Also, I believe their use has been deprecated in current drafts of the C++0x standard.
不。
以下是几个示例:
不可能使用异常规范编写模板代码,
副本可能会抛出异常,参数传递可能会抛出异常,
x()
可能会抛出一些未知的异常。异常规范往往会禁止可扩展性。
可能会演变成
你真的可以把它写成
<前><代码>抛出(...)
第一个是不可扩展的,第二个是过于雄心勃勃的,第三个正是您编写虚拟函数时的意思。
旧代码
当您编写依赖于另一个库的代码时,您并不真正知道当出现严重错误时它会做什么。
当
lib_f()
抛出异常时,g
将终止。 (在大多数情况下)这并不是您真正想要的。 永远不应该调用 std::terminate() 。 让应用程序因未处理的异常而崩溃(您可以从中检索堆栈跟踪)总是比默默地/暴力地死掉要好。编写返回常见错误并在异常情况下抛出的代码。
然而,当您的库只是抛出您自己的异常时,您可以使用异常规范来声明您的意图。
No.
Here are several examples why:
Template code is impossible to write with exception specifications,
The copies might throw, the parameter passing might throw, and
x()
might throw some unknown exception.Exception-specifications tend to prohibit extensibility.
might evolve into
You could really write that as
The first is not extensible, the second is overambitious and the third is really what you mean, when you write virtual functions.
Legacy code
When you write code which relies on another library, you don't really know what it might do when something goes horribly wrong.
g
will terminate, whenlib_f()
throws. This is (in most cases) not what you really want.std::terminate()
should never be called. It is always better to let the application crash with an unhandled exception, from which you can retrieve a stack-trace, than to silently/violently die.Write code that returns common errors and throws on exceptional occasions.
Nevertheless, when your library just throws your own exceptions, you can use exception specifications to state your intent.