例如,有两种不同的方法来访问私有数组的元素,重载数组下标运算符或定义at
:
T& operator[](size_t i) { return v[i]; }
T const& operator[](size_t i) const { return v[i]; }
T& at(size_t i)
{
if (i >= length)
throw out_of_range("You shall not pass!");
return v[i];
}
T const& at(size_t i) const
{
if (i >= length)
throw out_of_range("You shall not pass!");
return v[i];
}
Run Code Online (Sandbox Code Playgroud)
该at
版本可以抛出异常,但数组下标运算符不能.
我的问题是,尽管operator[]
没有抛出异常,它是否可以被标记为noexcept
甚至知道它可以引发SIGSEGV信号,还是只是一种不好的做法?
我想指出一个信号(如SIGSEGV)也不例外.作为文字解释noexcept
意义,一个noexcept
功能是声称它不会抛出异常.它什么也没说(包括信号).
但是,noexcept
函数具有更多的意义,至少对于我的代码的客户而言.noexcept
也隐含地说这个函数是安全的,它会在没有计算错误的情况下完成它的执行.
那么,标记noexcept
一个不安全的功能是不合适的呢?
这是一个棘手的问题,并提出了noexcept中涉及的一些关键问题- 为什么?来自Andrzej的C++博客说,我将尝试引用最小值(强调我的):
在这篇文章中,我想分享我对使用noexcept真正增加价值的观察.它不像人们预期的那么频繁,并且与投掷或不投掷异常没有多大关系.结论让我感到惊讶,并且我犹豫不决,因为这与我听到的有关该主题的权威人士的建议背道而驰.
和:
鉴于这种对noexcept的消极态度,它是否可以被视为有用?是.该noexcept功能引入很晚到C++ 11解决一个具体问题与移动语义.Douglas Gregor和David Abrahams在此描述过.
然后他接着给出了一个不寻常的移动分配定义并且认为我们真正想传达的不是它不会抛出异常而是它不会失败,但这是非常困难的问题,但它是出于真正的意图:
[...]这是因为除了真正意图传达的信息是功能永远不会失败; 不是它永远不会抛出!我们可以看到上面的函数可能会失败但仍然没有抛出,但它仍然符合noexcept(false)的条件.也许该关键字应该被称为nofail.编译器无法检查永不失败的保证(非常类似于任何其他故障安全保证),因此我们唯一能做的就是声明它.
这是一个更普遍的观察的一部分,我们感兴趣的是程序组件中的故障安全性,而不是异常安全性.无论你是否使用异常,错误返回值,错误或什么
除此之外,关于基本(无泄漏,不变保留),强(提交或回滚)和永不失败保证的推理应该仍然有效.
因此,如果我们采取类似的立场,似乎答案是否定的,如果不适合使用noexcept
,那似乎是你倾向于的地方.我不认为这是明确的答案.
他还注意到提案N3248:noexcept阻止库验证.这反过来又是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
.
归档时间: |
|
查看次数: |
770 次 |
最近记录: |