26

是的,我查看了我可以找到的C++ 标准(或草案),但我没有找到任何全面的 STL 容器提供的异常保证。我所能找到的只是偶尔出现的部分,其中对某些类型的某些功能的描述不完整。或者也许它在那里,但我只是没有找到它,我不知道。

注意:不是要一份人们能想到的所有保证的清单,基本上就是在这个问题中。
我正在寻找这些信息本身的权威来源——或者最好是一个免费版本的来源(例如标准草案),我可以或多或少地将其视为官方的。

4

3 回答 3

18

阅读标准可能会让人害怕(让我们回到标准),但是 Bjarne Stroustrup 在他的《C++ 编程语言》一书中就这个主题写了一个非常好的附录。他将此附录张贴在

http://www.stroustrup.com/3rd_safe0.html,位于 http://www.stroustrup.com/3rd_safe.pdf

它很长很详细(而且写得很好)。例如,您可能会发现第 E.4 节有趣,引用:

E.4 标准集装箱保证

如果库操作本身引发异常,它可以——并且确实——确保它所操作的对象处于明确定义的状态。例如, at() 为 vector 抛出 out_of_range (第 16.3.3 节)对于 vector 的异常安全来说不是问题。at() 的编写者可以毫无问题地确保向量在抛出之前处于明确定义的状态。

此外,第 E.4.1 节指出

除了基本的保证外,标准库还为一些插入或删除元素的操作提供了强有力的保证。

看看第 956 页。它包含一个保证向量、双端队列、列表和映射的各种操作的表。 总之,对这些容器的所有操作要么是 nothrow 要么是 strong ,除了提供基本保证的N 元素 insert into map 。

注意:上面的文字是旧的,不涉及 C++11,但对于大多数目标和目的来说应该仍然足够正确。

当涉及到 C++11...

标准首先声明,关于容器 array, deque, forward_list, list, vector, map, set, unordered_map, unordered_set, queue,stack:在

23.2.1/10

除非另有规定(见 23.2.4.1、23.2.5.1、23.3.3.4 和 23.3.6.5),本条款中定义的所有容器类型均满足以下附加要求:

— 如果 insert() 或 emplace() 函数在插入单个元素时抛出异常,则该函数无效。
— 如果 push_back() 或 push_front() 函数抛出异常,则该函数无效。
— 没有erase()、clear()、pop_back() 或pop_front() 函数抛出异常。
— 返回的迭代器的任何复制构造函数或赋值运算符都不会引发异常。
— 没有 swap() 函数抛出异常。
— 没有 swap() 函数会使任何引用被交换容器元素的引用、指针或迭代器失效。

上面提到的各个部分中指出的怪癖(每个都称为异常安全保证)主要是关于特殊的撞墙情况,例如在处理来自包含类型的散列、比较操作以及抛出交换和抛出移动的异常时操作。

于 2012-07-28T07:48:58.777 回答
10

n3376

23.2.1 一般容器要求 [container.requirements.general]

第 10 段

除非另有规定(见 23.2.4.1、23.2.5.1、23.3.3.4 和 23.3.6.5),本条款中定义的所有容器类型都满足以下附加要求:
— 如果 insert() 或 emplace() 抛出异常插入单个元素时的函数,该函数没有效果。
— 如果 push_back() 或 push_front() 函数抛出异常,则该函数无效。
— 没有erase()、clear()、pop_back() 或pop_front() 函数抛出异常。
— 返回的迭代器的任何复制构造函数或赋值运算符都不会引发异常。
— 没有 swap() 函数抛出异常。
— 没有 swap() 函数会使任何引用被交换容器元素的引用、指针或迭代器失效。
[注意:end() 迭代器不引用任何元素,因此它可能会失效。——尾注]

23.2.4 关联容器 [associative.reqmts]

23.2.4.1 异常安全保证 [associative.reqmts.except]

1 对于关联容器,没有 clear() 函数抛出异常。除非容器的 Compare 对象(如果有)抛出异常,否则 erase(k) 不会抛出异常。
2 对于关联容器,如果插入或 emplace 函数中插入单个元素的任何操作引发异常,则插入无效。
3 对于关联容器,没有交换函数会引发异常,除非该异常是由容器的比较对象(如果有)的交换引发的。

23.2.5 无序关联容器 [unord.req]

23.2.5.1 异常安全保证 [unord.req.except]

1 对于无序关联容器,没有 clear() 函数抛出异常。除非容器的 Hash 或 Pred 对象(如果有)抛出异常,否则 erase(k) 不会抛出异常。
2 对于无序关联容器,如果在插入单个元素的 insert 或 emplace 函数中除容器的散列函数之外的任何操作引发异常,则插入无效。
3 对于无序关联容器,没有交换函数抛出异常,除非该异常是由容器的 Hash 或 Pred 对象(如果有)的交换引发的。
4 对于无序关联容器,如果从容器的散列函数或比较函数之外的 rehash() 函数中抛出异常,则 rehash() 函数无效。

23.3.3.4 双端队列修饰符 [deque.modifiers]

无效 push_back(T&& x); 第 2 段

备注:如果不是由 T 的复制构造函数、移动构造函数、赋值运算符或移动赋值运算符引发异常,则没有任何影响。如果非 CopyInsertable T 的移动构造函数抛出异常,则未指定效果。

迭代器擦除(首先是 const_iterator,最后是 const_iterator);第 6 段

抛出:除非 T 的复制构造函数、移动构造函数、赋值运算符或移动赋值运算符抛出异常,否则什么都没有。

23.3.6.5 矢量修饰符 [vector.modifiers]

无效 push_back(T&& x); 第 2 段

如果非 CopyInsertable T 的移动构造函数抛出异常,则未指定效果。

迭代器擦除(首先是 const_iterator,最后是 const_iterator);第 5 段

抛出:除非 T 的复制构造函数、移动构造函数、赋值运算符或移动赋值运算符抛出异常,否则什么都没有。

于 2012-07-28T08:27:58.797 回答
2

您链接到的文档,n3337 标准草案,可以被视为官方文件。它是 C++11 标准加上小的编辑更改。

您只需要学习阅读标准,这是可以理解的,因为它并不易于阅读。

要查找任何特定库操作的异常保证,请检查该操作的规范以获取有关异常的注释和注释。如果该函数是成员函数,则检查类型规范以获取有关异常安全的注释以及它满足的要求。然后检查满足的要求,以了解对象必须做出的异常保证来满足这些要求。

对于泛型类型和算法,还要检查对模板参数的要求,以查看这些类型必须满足哪些要求才能使类型或算法或成员函数做出的所有异常保证都保持(如果模板参数不t 满足指定的要求,然后使用带有这些参数的模板具有未定义的行为,并且模板的规范都不适用)。

于 2012-07-28T20:25:20.500 回答