std::set::find异常保证

std::set::find exception guarantees

本文关键字:异常 find set std      更新时间:2023-10-16

我目前正在编写异常安全代码,我的设计不需要对set::find方法。我认为comparator对象总是成功的。这是否意味着set::find方法总是成功的?

根据http://en.cppreference.com/w/cpp/container/set/erase,set::erase方法,具有相同的假设,应该总是成功的,也许其中有一个发现(那么它显然值得在文档中发表评论!)

问题的直接来源是,我需要检查元素是否在集合中,并将其从集合中删除——所有这些都没有抛出保证(它在catch块中)。

std::set::find:

返回值

迭代到键为key的元素。如果找不到这样的元素,则返回over-the-end(请参见end())迭代器。

文档和C++标准都没有明确列出任何异常安全保证。然而,此处适用std::set::erase的相同规则(§23.2.4.1例外安全保证[相关要求除外]):

erase(k)不会抛出异常,除非该异常是由容器的Compare对象(如果有)抛出的。

本质上,除非Compare对象抛出异常,否则std::set::find不会抛出。Bjarne Stroustrup在C++编程语言特别版-附录E:中有以下几点要说

幸运的是,谓词很少做任何可能引发异常的事情。但是,在考虑异常安全性时,必须考虑用户定义的<==!=谓词。

如果您没有提供任何用户定义的谓词,您可以假设std::set::find没有抛出异常。如果是,则应将它们标记为noexcept,以便在您的场景中安全工作。

C++11§23.2.4.1异常安全保证[associal.reqmts.except]列出了关联容器(包括set)的所有异常安全要求,但没有提及find。所以不,标准并不能保证find永远不会抛出。

在没有未定义的行为并且假设有一个非抛出比较器的情况下,我发现C++标准库的实现极不可能存在,它将从set::find抛出异常。我个人认为(a)将set::find封装在noexcept转发函数中,这样如果发生这种"不可能"的事情,程序就会崩溃,或者(b)将特定的set::find调用封装在中

auto it = foo.end();
try {
it = foo.find(bar);
} catch(...) {}

并简单地将异常处理为"未找到">

请注意,关联容器的排序关系是未定义行为的一个容易被忽视的来源:§23.2.4/2要求排序关系在关键元素上诱导严格的弱排序(如§25.4所述)。使用而非严格弱排序的排序关系实例化的关联容器没有定义的行为,其一个可能的结果是从find引发异常。