【问题标题】:std::set::find exception guaranteesstd::set::find 异常保证
【发布时间】:2013-12-22 23:37:39
【问题描述】:

我目前正在编写异常安全代码,我的设计不需要任何抛出保证 set::find 方法。 我假设比较器对象总是成功的。 这是否意味着 set::find 方法总是会成功?

在我看到根据http://en.cppreference.com/w/cpp/container/set/erase,set::erase 方法,在相同的假设下,我想到了这种可能性,应该总是成功,也许其中有一个发现(那么它绝对值得评论在文档中!)

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

【问题讨论】:

  • 我不确定 set 或任何 stl 容器的标准或任何保证,但我会这么说。有一次我调用 vector::erase(v.end()).. 它抛出异常..我花了几个小时试图找出原因。原来我的向量是空的。不确定这是否仍然发生,或者 std::set 是否有这个问题,但它发生在我的 gcc 4.8.1 for windows 中。我最终不得不做一个 if 语句检查 vector::find != v.end()。我想你可能不得不这样做。这可能是你唯一一次确定没有例外?
  • @CantChoose 对于空向量和非空向量,这是未定义的行为。您无法删除末尾的第一个元素...
  • @CantChooseUsernames 你应该很高兴它抛出了异常。这是未定义的行为 AFAIK,可能已经擦除了您的硬盘;)

标签: c++ exception stl set


【解决方案1】:

std::set::find:

返回值

迭代器到带有键 key 的元素。如果没有找到这样的元素,则返回结束后(参见end())迭代器。

文档和 C++ 标准都没有明确列出任何异常安全保证。但是,此处适用于 std::set::erase 的相同规则(§23.2.4.1 异常安全保证 [associative.reqmts.except]):

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

本质上,除非Compare 对象抛出异常,否则std::set::find 不会抛出异常。 Bjarne Stroustrup 在The C++ Programming Language, Special Edition - Appendix E 中有以下话要说:

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

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

【讨论】:

    【解决方案2】:

    C++11 §23.2.4.1 异常安全保证 [associative.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 引发异常。

    【讨论】:

    • 我可以很容易地想象如果比较器没有正确返回对象的严格弱排序,则会引发异常。我打赌 Dev Studio 可能会。
    • @StilesCrisis 是的,抛出异常是程序中未定义行为的可能结果,就像任何其他可以想象的结果一样。
    • 写我的 operator
    • @StilesCrisis 单独存在并不是未定义的行为。如果您使用 operator< 作为标准库关联容器的排序关系,那么您的程序没有定义的行为,因为第 23.2.4/2 节需要这种排序关系来对键类型的元素进行严格的弱排序.
    • 从技术角度来看,您所说的一切都是正确的,但这对 OP 的问题没有帮助。如果你想讨论find 可能会抛出而不是成功的情况,这绝对是这样的情况,而且这是一个常见的错误。如果您想避免输入find,则需要 100% 确保您的比较器始终返回严格的弱排序。
    猜你喜欢
    • 1970-01-01
    • 2021-10-04
    • 2012-02-06
    • 1970-01-01
    • 2014-04-04
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-09-04
    相关资源
    最近更新 更多