C++ 在函数的签名中抛出关键字
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/1055387/
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
Throw keyword in function's signature
提问by Konstantin
What is the technical reason why it is considered bad practice to use the C++ throw
keyword in a function signature?
throw
在函数签名中使用 C++关键字被认为是不好的做法的技术原因是什么?
bool some_func() throw(myExc)
{
...
if (problem_occurred)
{
throw myExc("problem occurred");
}
...
}
采纳答案by jalf
No, it is not considered good practice. On the contrary, it is generally considered a bad idea.
不,这不被认为是好的做法。相反,它通常被认为是一个坏主意。
http://www.gotw.ca/publications/mill22.htmgoes into a lot more detail about why, but the problem is partly that the compiler is unable to enforce this, so it has to be checked at runtime, which is usually undesirable. And it is not well supported in any case. (MSVC ignores exception specifications, except throw(), which it interprets as a guarantee that no exception will be thrown.
http://www.gotw.ca/publications/mill22.htm详细介绍了原因,但问题的部分原因在于编译器无法强制执行此操作,因此必须在运行时进行检查,这通常是不受欢迎的。它在任何情况下都没有得到很好的支持。(MSVC 忽略异常规范,throw() 除外,它将其解释为保证不会抛出异常。
回答by sth
Jalf already linked to it, but the GOTWputs it quite nicely why exception specifications are not as useful as one might hope:
Jalf 已经链接到它,但是GOTW很好地说明了为什么异常规范不像人们希望的那样有用:
int Gunc() throw(); // will throw nothing (?)
int Hunc() throw(A,B); // can only throw A or B (?)
Are the comments correct? Not quite.
Gunc()
may indeed throw something, andHunc()
may well throw something other than A or B! The compiler just guarantees to beat them senseless if they do… oh, and to beat your program senseless too, most of the time.
评论正确吗?不完全的。
Gunc()
可能确实会抛出一些东西,并且很Hunc()
可能会抛出 A 或 B 以外的东西!编译器只是保证在他们这样做时毫无意义地击败他们......哦,而且大多数时候也会毫无意义地击败你的程序。
That's just what it comes down to, you probably just will end up with a call to terminate()
and your program dying a quick but painful death.
这就是它归结起来的原因,您可能最终会调用terminate()
并且您的程序会快速但痛苦地死亡。
The GOTWs conclusion is:
GOTW 的结论是:
So here's what seems to be the best advice we as a community have learned as of today:
- Moral #1:Never write an exception specification.
- Moral #2:Except possibly an empty one, but if I were you I'd avoid even that.
所以这似乎是我们作为一个社区今天学到的最好的建议:
- 道德 #1:永远不要编写异常规范。
- 道德#2:除了可能是空的,但如果我是你,我什至会避免这样做。
回答by John Doe
To add a bit more value to all the other answer's to this question, one should invest a few minutes in the question: What is the output of the following code?
为了给这个问题的所有其他答案增加一点价值,应该在这个问题上投入几分钟:以下代码的输出是什么?
#include <iostream>
void throw_exception() throw(const char *)
{
throw 10;
}
void my_unexpected(){
std::cout << "well - this was unexpected" << std::endl;
}
int main(int argc, char **argv){
std::set_unexpected(my_unexpected);
try{
throw_exception();
}catch(int x){
std::cout << "catch int: " << x << std::endl;
}catch(...){
std::cout << "catch ..." << std::endl;
}
}
Answer: As noted here, the program calls std::terminate()
and thus none of the exception handlers will get called.
答:正如这里所指出的,程序会调用std::terminate()
,因此不会调用任何异常处理程序。
Details: First my_unexpected()
function is called, but since it doesn't re-throw a matching exception type for the throw_exception()
function prototype, in the end, std::terminate()
is called. So the full output looks like this:
详细信息:第一个 my_unexpected()
函数被调用,但由于它没有为throw_exception()
函数原型重新抛出匹配的异常类型,因此最终std::terminate()
被调用。所以完整的输出如下所示:
user@user:~/tmp$ g++ -o except.test except.test.cpp
user@user:~/tmp$ ./except.test
well - this was unexpected
terminate called after throwing an instance of 'int'
Aborted (core dumped)
user@user:~/tmp$ g++ -o except.test except.test.cpp
user@user:~/tmp$ ./except.test
well - 这是
在抛出一个 'int'
Aborted实例后调用的意外终止(核心倾倒)
回答by Paolo Tedesco
The only practical effect of the throw specifier is that if something different from myExc
is thrown by your function, std::unexpected
will be called (instead of the normal unhandled exception mechanism).
throw 说明符的唯一实际作用是,如果myExc
您的函数抛出了与不同的内容,std::unexpected
则将调用(而不是正常的未处理异常机制)。
To document the kind of exceptions that a function can throw, I typically do this:
为了记录函数可以抛出的异常类型,我通常这样做:
bool
some_func() /* throw (myExc) */ {
}
回答by Greg D
When throw specifications were added to the language it was with the best intentions, but practice has borne out a more practical approach.
当 throw 规范被添加到语言中时,它是出于最好的意图,但实践证明了一种更实用的方法。
With C++, my general rule of thumb is to only use throw specifications to indicate that a method can't throw. This is a strong guarantee. Otherwise, assume it could throw anything.
对于 C++,我的一般经验法则是仅使用抛出规范来指示方法不能抛出。这是一个强有力的保证。否则,假设它可以抛出任何东西。
回答by Pratik Singhal
Well, while googling about this throw specification, I had a look at this article :- (http://blogs.msdn.com/b/larryosterman/archive/2006/03/22/558390.aspx)
好吧,在谷歌搜索这个抛出规范时,我看了这篇文章:- ( http://blogs.msdn.com/b/larryosterman/archive/2006/03/22/558390.aspx)
I am reproducing a part of it here also, so that it can be used in future irrespective of the fact that the above link works or not.
我也在此处复制其中的一部分,以便将来无论上述链接是否有效都可以使用它。
class MyClass
{
size_t CalculateFoo()
{
:
:
};
size_t MethodThatCannotThrow() throw()
{
return 100;
};
void ExampleMethod()
{
size_t foo, bar;
try
{
foo = CalculateFoo();
bar = foo * 100;
MethodThatCannotThrow();
printf("bar is %d", bar);
}
catch (...)
{
}
}
};
When the compiler sees this, with the "throw()" attribute, the compiler can completely optimize the "bar" variable away, because it knows that there is no way for an exception to be thrown from MethodThatCannotThrow(). Without the throw() attribute, the compiler has to create the "bar" variable, because if MethodThatCannotThrow throws an exception, the exception handler may/will depend on the value of the bar variable.
当编译器看到这一点时,通过“throw()”属性,编译器可以完全优化掉“bar”变量,因为它知道无法从 MethodThatCannotThrow() 抛出异常。如果没有 throw() 属性,编译器必须创建“bar”变量,因为如果 MethodThatCannotThrow 抛出异常,则异常处理程序可能/将取决于 bar 变量的值。
In addition, source code analysis tools like prefast can (and will) use the throw() annotation to improve their error detection capabilities - for example, if you have a try/catch and all the functions you call are marked as throw(), you don't need the try/catch (yes, this has a problem if you later call a function that could throw).
此外,像 prefast 这样的源代码分析工具可以(并且将)使用 throw() 注释来提高它们的错误检测能力——例如,如果你有一个 try/catch 并且你调用的所有函数都被标记为 throw(),你不需要 try/catch (是的,如果你以后调用一个可能抛出的函数,这会有问题)。
回答by Pablo Adames
A no throw specification on an inlined function that only returns a member variable and could not possibly throw exceptions may be used by some compilers to do pessimizations(a made-up word for the opposite of optimizations) that can have a detrimental effect on performance. This is described in the Boost literature: Exception-specification
某些编译器可能会使用对仅返回成员变量且不可能抛出异常的内联函数的 no throw 规范来进行可能对性能产生不利影响的悲观化(与优化相反的虚构词)。这在 Boost 文献中有描述:异常规范
With some compilersa no-throw specification on non-inline functions may be beneficial if the correct optimizations are made and the use of that function impacts performance in a way that it justifies it.
对于某些编译器,如果进行了正确的优化并且该函数的使用以证明其合理性的方式影响性能,则对非内联函数的不抛出规范可能是有益的。
To me it sounds like whether to use it or not is a call made by a very critical eye as part of a performance optimization effort, perhaps using profiling tools.
对我来说,作为性能优化工作的一部分,是否使用它听起来像是一个非常挑剔的眼睛发出的呼吁,也许使用分析工具。
A quote from the above link for those in a hurry (contains an example of bad unintended effects of specifying throwon an inline function from a naive compiler):
来自上述链接的引用,适用于那些匆忙的人(包含一个示例,说明在天真的编译器的内联函数上指定throw 的意外不良影响):
Exception-specification rationale
Exception specifications [ISO 15.4] are sometimes coded to indicate what exceptions may be thrown, or because the programmer hopes they will improve performance. But consider the following member from a smart pointer:
T& operator*() const throw() { return *ptr; }
This function calls no other functions; it only manipulates fundamental data types like pointers Therefore, no runtime behavior of the exception-specification can ever be invoked. The function is completely exposed to the compiler; indeed it is declared inline Therefore, a smart compiler can easily deduce that the functions are incapable of throwing exceptions, and make the same optimizations it would have made based on the empty exception-specification. A "dumb" compiler, however, may make all kinds of pessimizations.
For example, some compilers turn off inlining if there is an exception-specification. Some compilers add try/catch blocks. Such pessimizations can be a performance disaster which makes the code unusable in practical applications.
Although initially appealing, an exception-specification tends to have consequences that require very careful thought to understand. The biggest problem with exception-specifications is that programmers use them as though they have the effect the programmer would like, instead of the effect they actually have.
A non-inline function is the one place a "throws nothing" exception-specification may have some benefit with some compilers.
异常规范的基本原理
异常规范 [ISO 15.4] 有时被编码以指示可能抛出哪些异常,或者因为程序员希望它们能提高性能。但是从智能指针考虑以下成员:
T& operator*() const throw() { return *ptr; }
该函数不调用其他函数;它只操作像指针这样的基本数据类型,因此,不能调用异常规范的任何运行时行为。函数完全暴露给编译器;实际上它被声明为 inline 因此,一个聪明的编译器可以很容易地推断出这些函数不能抛出异常,并根据空异常规范进行相同的优化。然而,一个“愚蠢的”编译器可能会做出各种悲观的看法。
例如,如果有异常规范,一些编译器会关闭内联。一些编译器添加了 try/catch 块。这种悲观可能是性能灾难,使代码在实际应用中无法使用。
尽管最初很有吸引力,但异常规范往往会产生需要非常仔细思考才能理解的后果。异常规范的最大问题是程序员使用它们就好像它们具有程序员想要的效果,而不是它们实际具有的效果。
非内联函数是“什么都不抛出”异常规范可能对某些编译器有一些好处的地方。