如何跟踪每个函数提供的异常安全保证

How do you keep track of exception safety guarantees offered by each function

本文关键字:异常 安全 函数 何跟踪 跟踪      更新时间:2023-10-16

在编写异常安全代码时,有必要考虑所有调用函数的异常安全保证(无、基本、强或无抛出)。由于编译器没有提供任何帮助,我认为函数命名约定在这里可能会有所帮助。是否有某种既定的符号标准来指示函数提供的异常安全保证的级别?我在想匈牙利式的东西:

void setFooB(Foo const& s); // B, offers basic guarantee
int computeSomethingS();    // S, offers strong guarantee
int getDataNT() throws();   // NT, offers no-throw
void allBetsAreOffN();      // N, offers no guarantee
编辑:我同意这种命名惯例很难看的评论,所以请允许我详细说明我建议加入的原因。

假设我重构了一些代码,在这个过程中,改变了函数提供的异常安全级别。如果保证从强变为基本(可能是由于速度的提高),那么每个调用重构函数的函数都必须重新考虑异常安全性。如果保证的更改也触发了函数名称的更改,那么它将允许编译器帮助我至少标记更改后的函数的所有使用。这就是我建议上述命名约定的基本原理,尽管它有问题。这与const非常相似,在const中,函数的const属性的改变会对其他调用函数产生级联效应,但在这种情况下,编译器会提供非常有效的帮助。

所以我想我的问题是,人们养成了什么样的工作习惯,以确保代码实际上实现了预期的异常保证,特别是在代码维护和重构期间。

我通常发现自己在注释(doxygen)中记录了这一点,除了不抛出保证外,当且仅当确定函数保证不抛出时,我经常标记throw() 异常规范,异常安全性很重要。

也就是说,我通常更担心代码部分的异常,其中未处理的异常会导致问题,并在本地处理(通过其他方式确保您的代码是异常安全的,如RAII,在外部执行工作,然后将结果合并为无抛出操作-即无抛出交换,这是我积极标记为throw()的唯一函数。

其他人可能有不同的经历,但我认为这对我的日常工作来说已经足够了。

我认为你不需要做任何特别的事情。

我唯一真正记录的是no-throw,这是因为语言的语法允许它。

项目中不应该有不提供保证的代码。所以只剩下强/基本的文件。对于这些,我认为你不需要显式地调用它,因为它实际上不是关于方法本身,而是关于整个类(对于这两个保证)。它们提供的保证实际上取决于使用情况。

我愿意认为我对所有事情都提供了强有力的保证(但我没有),有时它太昂贵了,有时它只是不值得努力(如果东西要抛出,它们无论如何都会被破坏)。

我理解你想做好的意愿,但我不确定这样的命名惯例。

一般来说,我对那些没有被语言强制执行的命名约定很警惕:它们很容易成为最大的骗子。

如果你真的需要这样的东西,我的建议是得到一个编译器(例如Clang),并添加一组新的属性。请注意,您需要编辑标准库提供的头文件,以及您所依赖的所有第三方头文件,对它们进行注释,以便您可以从头开始获得这些保证。

然后你可以让编译器检查注释(也不会是微不足道的…),然后注释变得有用,因为它们不能

我正在考虑添加

@par Exception Safety
Strong guarantee