我应该在 C++ 中使用异常说明符吗?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/88573/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Should I use an exception specifier in C++?
提问by 1800 INFORMATION
In C++, you can specify that a function may or may not throw an exception by using an exception specifier. For example:
在 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
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.
- 编译器并没有真正以任何严格的方式强制执行异常说明符,因此好处并不大。理想情况下,您希望得到编译错误。
- 如果函数违反了异常说明符,我认为标准行为是终止程序。
- 在 VS.Net 中,它将 throw(X) 视为 throw(...),因此对标准的遵守不强。
Do you think exception specifiers should be used?
Please answer with "yes" or "no" and provide some reasons to justify your answer.
你认为应该使用异常说明符吗?
请回答“是”或“否”,并提供一些理由来证明您的回答是正确的。
采纳答案by Christopher
No.
不。
Here are several examples why:
以下是几个原因:
Template code is impossible to write with exception specifications,
template<class T> void f( T k ) { T x( k ); x.x(); }
The copies might throw, the parameter passing might throw, and
x()
might throw some unknown exception.Exception-specifications tend to prohibit extensibility.
virtual void open() throw( FileNotFound );
might evolve into
virtual void open() throw( FileNotFound, SocketNotReady, InterprocessObjectNotImplemented, HardwareUnresponsive );
You could really write that as
throw( ... )
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.
int lib_f(); void g() throw( k_too_small_exception ) { int k = lib_f(); if( k < 0 ) throw k_too_small_exception(); }
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.
Error e = open( "bla.txt" ); if( e == FileNotFound ) MessageUser( "File bla.txt not found" ); if( e == AccessDenied ) MessageUser( "Failed to open bla.txt, because we don't have read rights ..." ); if( e != Success ) MessageUser( "Failed due to some other error, error code = " + itoa( e ) ); try { std::vector<TObj> k( 1000 ); // ... } catch( const bad_alloc& b ) { MessageUser( "out of memory, exiting process" ); throw; }
模板代码不可能写出异常规范,
template<class T> void f( T k ) { T x( k ); x.x(); }
副本可能会抛出,参数传递可能会抛出,并且
x()
可能会抛出一些未知的异常。异常规范倾向于禁止可扩展性。
virtual void open() throw( FileNotFound );
可能演变成
virtual void open() throw( FileNotFound, SocketNotReady, InterprocessObjectNotImplemented, HardwareUnresponsive );
你真的可以这样写
throw( ... )
第一个是不可扩展的,第二个是野心勃勃的,第三个是你真正的意思,当你写虚函数时。
遗留代码
当你编写依赖于另一个库的代码时,你真的不知道当出现可怕的错误时它会做什么。
int lib_f(); void g() throw( k_too_small_exception ) { int k = lib_f(); if( k < 0 ) throw k_too_small_exception(); }
g
将终止,当lib_f()
抛出。这(在大多数情况下)不是您真正想要的。std::terminate()
永远不应该被调用。让应用程序因未处理的异常而崩溃,您可以从中检索堆栈跟踪,总比静默/暴力死亡要好。编写返回常见错误并在异常情况下抛出的代码。
Error e = open( "bla.txt" ); if( e == FileNotFound ) MessageUser( "File bla.txt not found" ); if( e == AccessDenied ) MessageUser( "Failed to open bla.txt, because we don't have read rights ..." ); if( e != Success ) MessageUser( "Failed due to some other error, error code = " + itoa( e ) ); try { std::vector<TObj> k( 1000 ); // ... } catch( const bad_alloc& b ) { MessageUser( "out of memory, exiting process" ); throw; }
Nevertheless, when your library just throws your own exceptions, you can use exception specifications to state your intent.
然而,当您的库只是抛出您自己的异常时,您可以使用异常规范来说明您的意图。
回答by Michael Burr
Avoid exception specifications in C++. The reasons you give in your question are a pretty good start for why.
避免 C++ 中的异常规范。你在问题中给出的原因是一个很好的开始。
See Herb Sutter's "A Pragmatic Look at Exception Specifications".
参见 Herb Sutter 的“异常规范的务实观察”。
回答by Martin York
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
我认为标准的 except 约定(对于 C++)
异常说明符是 C++ 标准中的一个实验,但大部分都失败了。
例外是 no throw 说明符很有用,但您还应该在内部添加适当的 try catch 块以确保代码与说明符匹配。Herb Sutter 有一个关于这个主题的页面。第 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:
无保证:
There is no guarantee about the state of the object after an exception escapes a method
In these situations the object should no longer be used.
在异常转义方法之后,无法保证对象的状态
在这些情况下,不应再使用对象。
Basic Guarantee:
基本保证:
In nearly all situations this should be the minimum guarantee a method provides.
This guarantees the object's state is well defined and can still be consistently used.
在几乎所有情况下,这应该是方法提供的最低保证。
这保证了对象的状态被很好地定义并且仍然可以被一致地使用。
Strong Guarantee: (aka Transactional Guarantee)
强力保证:(又名交易保证)
This guarantees that the method will complete successfully
Or an Exception will be thrown and the objects state will not change.
这保证了方法会成功完成
或者抛出异常并且对象状态不会改变。
No Throw Guarantee:
无投掷保证:
The method guarantees that no exceptions are allowed to propagate out of the method.
All destructors should make this guarantee.
| N.B. If an exception escapes a destructor while an exception is already propagating
| the application will terminate
该方法保证不允许异常传播到该方法之外。
所有的析构函数都应该做出这个保证。
| 注意如果异常已经在传播时转义析构函数
| 申请将终止
回答by Jeremy
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.
当您违反异常规范时,gcc 将发出警告。我所做的是仅在“lint”模式下使用宏来使用异常规范,明确编译以进行检查以确保异常与我的文档一致。
回答by Harold Ekstrom
The only useful exception specifier is "throw()", as in "doesn't throw".
唯一有用的异常说明符是“throw()”,如“不抛出”。
回答by coppro
Exception specifications are not wonderfully useful tools in C++. However, there /is/ a good use for them, if combined with std::unexpected.
异常规范在 C++ 中并不是非常有用的工具。但是,如果与 std::unexpected 结合使用,/is/ 对它们有很好的用途。
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.
我在一些项目中所做的是带有异常规范的代码,然后使用一个函数调用 set_unexpected() ,该函数将抛出我自己设计的特殊异常。此异常在构造时获得回溯(以特定于平台的方式)并从 std::bad_exception 派生(以允许它在需要时传播)。如果它像往常一样导致 terminate() 调用,则回溯由 what() 打印(以及导致它的原始异常;不难找到),所以我得到了我的合同在哪里的信息违反了,比如抛出了什么意外的库异常。
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).
如果我这样做,我从不允许传播库异常(std 除外)并从 std::exception 派生我的所有异常。如果一个库决定抛出,我会捕捉并转换成我自己的层次结构,这样我就可以始终控制代码。出于显而易见的原因,调用依赖函数的模板函数应该避免异常规范;但无论如何,很少有带有库代码的模板化函数接口(并且很少有库真正以有用的方式使用模板)。
回答by Ferruccio
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.
此外,我相信它们的使用在 C++0x 标准的当前草案中已被弃用。
回答by TheJuice
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:
“throw()”规范允许编译器在进行代码流分析时执行一些优化,前提是它知道该函数永远不会抛出异常(或至少承诺永远不会抛出异常)。拉里·奥斯特曼 (Larry Osterman) 在这里简要讨论了这一点:
http://blogs.msdn.com/larryosterman/archive/2006/03/22/558390.aspx
http://blogs.msdn.com/larryosterman/archive/2006/03/22/558390.aspx
回答by Branan
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.
否则,我不认为使用任何东西特别有用,只是throw()
表明它不会引发任何异常。
回答by user10392
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.
是的,如果您喜欢内部文档。或者也许编写一个其他人将使用的库,以便他们可以在不查阅文档的情况下知道发生了什么。抛出或不抛出可以被认为是 API 的一部分,就像返回值一样。
I agree, they are not really useful for enforcing correctness Java style in the compiler, but it's better than nothing or haphazard comments.
我同意,它们对于在编译器中强制执行 Java 风格的正确性并不是很有用,但总比没有或随意评论要好。