首先,正如其他人所说,在C ++,恕我直言,事情并没有那么清晰,主要是因为C ++的要求和限制比其他语言(尤其是C ++)多一些。具有“相似”异常问题的C#和Java。
我将在std :: stof示例中公开:
将空字符串传递给std :: stof(将抛出invalid_argument)不是编程错误
如我所见,此函数的基本协定是它试图将其参数转换为浮点数,并且任何这样做的失败均由异常报告。两种可能的异常都源自logic_error
但不是程序员错误的意思,而是“输入永远无法转换为浮点数”的意思。
在这里,可能有人说a logic_error
表示(运行时)输入,尝试对其进行转换始终是错误的-但是函数的工作是确定并告诉您(通过异常)。
旁注:在这种情况下,runtime_error
可以将a视为在给函数提供相同输入的情况下,理论上可以针对不同的运行成功的事物。(例如,文件操作,数据库访问等)
补充说明:C ++ regex库选择从中导出错误,runtime_error
尽管在某些情况下可以将其分类为此处(无效的regex模式)。
恕我直言,这只是表明在C ++中分组logic_
或runtime_
错误是相当模糊的,并且在一般情况下并没有太大帮助(*)-如果您需要处理特定的错误,则可能需要抓住两者之间的差点。
(*):这是不是说,单件的代码不应该是一致的,但不管你扔runtime_
或logic_
或custom_
出头真的没有那么重要,我想。
要评论stof
和bitset
:
这两个函数都将字符串作为参数,在两种情况下均为:
- 检查调用方给定的字符串是否有效非常简单(例如,最坏的情况下,您必须复制函数逻辑;对于位集,尚不能立即清除空字符串是否有效,因此请ctor决定)
- 函数已经负责“解析”字符串,因此它已经必须验证字符串,因此有意义的是它报告了错误以统一“使用”字符串(在两种情况下,这都是一个例外) 。
经常出现异常的规则是“仅在特殊情况下使用异常”。但是,库函数应该如何知道哪些情况例外?
恕我直言,此语句有两个根源:
性能:如果在关键路径中调用了函数,并且“例外”情况并不特殊,即,大量的传递将涉及引发异常,那么每次为异常清盘机器付费都没有道理,并且可能太慢。
错误处理局部性:如果一个函数被调用,异常立即引起和处理,那么在抛出异常的小点,因为错误处理会更加冗长用的catch
比使用if
。
例:
float readOrDefault;
try {
readOrDefault = stof(...);
} catch(std::exception&) {
// discard execption, just use default value
readOrDefault = 3.14f; // 3.14 is the default value if cannot be read
}
这里是TryParse
vs 之类的功能Parse
发挥作用的地方:一种版本是当本地代码期望已解析的字符串有效时,一种版本是当本地代码假定实际期望(即非例外)解析失败时。
确实,stof
只是(定义为)的包装strtof
,因此,如果您不希望出现异常,请使用该包装。
因此,我应该如何决定是否应为特定功能使用异常?
恕我直言,您有两种情况:
“库”之类的函数(经常在不同的上下文中重复使用):您基本上无法决定。可能同时提供两个版本,可能是一个报告错误的版本,另一个是将返回的错误转换为异常的包装器。
“应用程序”功能(特定于一堆应用程序代码,可能会重用一些,但受应用程序错误处理风格的限制,等等):在这里,它通常应该很清楚。如果调用函数的代码路径以合理且有用的方式处理异常,请使用异常报告任何(但请参见下文)错误。如果应用程序代码对于错误返回样式更容易读写,则一定要使用它。
当然,介于两者之间-只需使用需要的内容并记住YAGNI。
最后,我想我应该回到FAQ声明,
不要使用throw来指示函数使用中的编码错误。使用断言或其他机制将进程发送到调试器或使进程崩溃...
对于所有明确表示某件事已严重混乱或调用代码显然不知道自己在做什么的错误,我表示赞同。
但是当这是适当的往往是高度专用的,因此见上图书馆领域与应用领域。
这是关于是否以及如何验证调用先决条件的问题,但我不愿赘述,回答已经太久了:-)