这是一个棘手的问题,并提出了noexcept 中涵盖的一些关键问题——为了什么?来自 Andrzej 的 C++ 博客,其中说,我将尝试在此处引用最小值(强调我的):
在这篇文章中,我想分享我对使用 noexcept 真正增加价值的地方的观察。它比人们预期的要少,并且与抛出或不抛出异常没有太大关系。这个结论让我有点吃惊,我犹豫是否要提出它,因为这与我从我认为有关该主题的权威人士那里听到的建议背道而驰。
和:
鉴于对 noexcept 的这种消极态度,它是否可以被认为是有用的?是的。noexcept功能很晚才引入 C++11,以解决移动语义的一个特定问题。Douglas Gregor 和 David Abrahams 在这里描述了它。
然后他继续给出了一个不寻常的移动分配定义,并认为我们真正想要传达的不是它不会抛出异常而是它不会失败,但这是一个非常困难的问题,但它是真正的意图:
[...]这是因为 noexcept 真正要传达的信息是该功能永远不会失败;并不是说它从不抛出!我们可以在上面看到一个函数可能会失败但仍然不会抛出,但它仍然符合 noexcept(false) 的条件。也许关键字应该被称为 nofail。编译器无法检查永不失败的保证(就像任何其他故障安全保证一样),因此我们唯一能做的就是声明它。
这是一个更普遍的观察的一部分,我们感兴趣的是程序组件中的故障安全而不是异常安全。无论你使用异常、错误返回值、errno 还是什么
无论如何,关于基本(无泄漏,保持不变),强(提交或回滚)和永不失败保证的推理仍然应该成立。
因此,如果我们采取类似的立场,那么答案似乎是否定的,如果不适合使用noexcept
,那似乎就是你所倾向的地方。我不认为这是一个明确的答案。
他还注意到提案 N3248:noexcept Prevents Library Validation。这反过来又是N3279 的基础:在库中保守使用 noexcept。这篇论文定义了窄合约和宽合约,就像 N3248 一样:
宽合约
函数或操作的广泛契约不指定任何未定义的行为。这样的契约没有先决条件:具有广泛契约的函数对其参数、任何对象状态或任何外部全局状态都没有额外的运行时约束。具有广泛合同的函数的示例是 vector::begin() 和 vector::at(size_type) 。没有广泛合同的函数的例子是 vector::front() 和 vector::operator[](size_type) 。
窄合约
狭义合同是不广泛的合同。当以违反记录的合同的方式调用时,函数或操作的狭窄合同会导致未定义的行为。这样的契约至少规定了一个涉及其参数、对象状态或一些外部全局状态的先决条件,例如静态对象的初始化。具有窄合约的标准函数的好例子是 vector::front() 和 vector::operator[](size_type) 。
并建议:
LWG 同意不能抛出的具有广泛合同的每个库函数都应标记为无条件 noexcept。
并暗示不应具有狭窄合同的功能noexcept
,这得到LWG 问题 2337的支持,其中说:
[...]这些设计考虑超越了我们反对 noexcept 窄合约功能的一般政策。[...]
因此,如果我们想保守一点并遵循标准库实践,那么似乎因为operator[]
没有广泛的合同,它不应该被标记noexcept
。