Update item14.md

This commit is contained in:
猫耳堀川雷鼓 2021-02-07 16:58:33 +08:00 committed by GitHub
parent d3e3e45d7d
commit b108e982a0
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

View File

@ -69,7 +69,7 @@ struct pair {
请注意我说有些函数有**自然的**`noexcept`实现法。为了`noexcept`而扭曲函数实现来达成目的是本末倒置。是把马车放到马前,是一叶障目不见泰山。是...选择你喜欢的比喻吧。(译注:几个英语熟语,都是想说明“本末倒置”。)如果一个简单的函数实现可能引发异常(比如调用一个可能抛异常的函数),而你为了讨好调用者隐藏了这个(比如捕获所有异常,然后替换为状态码或者特殊返回值),这不仅会使你的函数实现变得复杂,还会让调用点的代码变得复杂。调用者可能不得不检查状态码或特殊返回值。而这些复杂的运行时开销(比如额外的分支,大的函数给指令缓存带来的压力等)可能超出`noexcept`带来的性能提升,再加上你会悲哀的发现这些代码又难读又难维护。那是糟糕的软件工程化。
对于一些函数,使其成为`noexcept`是很重要的它们应当默认如是。在C++98允许内存释放memory deallocation函数即`operator delete`和`operator delete[]`和析构函数抛出异常是糟糕的代码设计C++11将这种作风升级为语言规则。默认情况下内存释放函数和析构函数——不管是用户定义的还是编译器生成的——都是隐式`noexcept`。因此它们不需要声明``noexcept`。(这么做也不会有问题,只是不合常规)。析构函数非隐式`noexcept`的情况仅当类的数据成员(包括继承的成员还有继承成员内的数据成员)明确声明它的析构函数可能抛出异常(如声明“`noexcept(false)`”)。这种析构函数不常见,标准库里面没有。如果一个对象的析构函数可能被标准库使用(比如在容器内或者被传给一个算法),析构函数又可能抛异常,那么程序的行为是未定义的。
对于一些函数,使其成为`noexcept`是很重要的它们应当默认如是。在C++98允许内存释放memory deallocation函数即`operator delete`和`operator delete[]`和析构函数抛出异常是糟糕的代码设计C++11将这种作风升级为语言规则。默认情况下内存释放函数和析构函数——不管是用户定义的还是编译器生成的——都是隐式`noexcept`。因此它们不需要声明`noexcept`。(这么做也不会有问题,只是不合常规)。析构函数非隐式`noexcept`的情况仅当类的数据成员(包括继承的成员还有继承成员内的数据成员)明确声明它的析构函数可能抛出异常(如声明“`noexcept(false)`”)。这种析构函数不常见,标准库里面没有。如果一个对象的析构函数可能被标准库使用(比如在容器内或者被传给一个算法),析构函数又可能抛异常,那么程序的行为是未定义的。
值得注意的是一些库接口设计者会区分有宽泛契约(**wild contracts**)和严格契约(**narrow contracts**)的函数。有宽泛契约的函数没有前置条件。这种函数不管程序状态如何都能调用,它对调用者传来的实参不设约束。(“不管程序状态如何”和“不设约束”对已经行为未定义的程序无效。比如`std::vector::size`有宽泛契约,但是并不保证如果你把一块随机内存转换为一个`std::vector`,在这块内存上调用它会有合理的表现。转换的结果是未定义的,所以包含这个转换的程序也无法保证表现合理)宽泛契约的函数决不表现出未定义行为。
@ -82,7 +82,7 @@ struct pair {
void f(const std::string& s) noexcept; //前置条件:
//s.length() <= 32
```
假定`f`的实现者在函数里面检查前置条件冲突。虽然检查是没有必要的但是也没禁止这么做检查前置条件可能也是有用的比如在系统测试时。debug一个抛出的异常一般都比跟踪未定义行为起因更容易。那么怎么报告前置条件冲突使得测试工具或客户端错误处理程序能检测到它呢简单直接的做法是抛出`"precondition was violated"`异常,但是如果`f`声明了`noexcept`,这就行不通了;抛出一个异常会导致程序终止。因为这个原因,区分严格/宽泛契约库设计者一般会将`noexcept`留给宽泛契约函数。
假定`f`的实现者在函数里面检查前置条件冲突。虽然检查是没有必要的但是也没禁止这么做检查前置条件可能也是有用的比如在系统测试时。debug一个抛出的异常一般都比跟踪未定义行为起因更容易。那么怎么报告前置条件冲突使得测试工具或客户端错误处理程序能检测到它呢简单直接的做法是抛出“precondition was violated”异常,但是如果`f`声明了`noexcept`,这就行不通了;抛出一个异常会导致程序终止。因为这个原因,区分严格/宽泛契约库设计者一般会将`noexcept`留给宽泛契约函数。
作为结束语,让我详细说明一下之前的观察,即编译器不会为函数实现和异常规范提供一致性保障。考虑下面的代码,它是完全正确的:
```cpp