在哪里可以找到标准容器和算法的所有例外保证?

Where can I find all the exception guarantees for the Standard Containers and Algorithms?

提问人:user541686 提问时间:7/28/2012 最后编辑:Communityuser541686 更新时间:3/13/2015 访问量:4758

问:

是的,我已经查看了我能找到的 C++ 标准(或草案),但我没有找到任何全面的 STL 容器提供的例外保证。我能找到的只是偶尔的部分,其中某些类型的某些函数的描述不完整。或者也许它在那里,但我只是没有找到它,我不知道。

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

C 标准库 C++-FAQ 异常安全

评论

0赞 user541686 7/28/2012
@bames53:例子?“保证了底层类型是可移动的,则具有很强的异常安全性;否则,它保证了基本的异常安全。我在我放在这里的标准链接中找不到它。vector::swap
0赞 bames53 7/28/2012
我在标准中也没有看到这种保证。是什么让你认为这是有保证的?我不认为是,元素类型是否可移动似乎与 vector::swap 无关,因为 vector::swap 需要在恒定时间内运行,这意味着通常它不能进行元素交换。
1赞 user541686 7/29/2012
@bames53:我的错,这是一个错别字,我的意思是(可以在下面使用)。无论如何,我没有说这是有保证的,我只是给出了一个我正在寻找的东西的潜在例子vector::insertstd::swap
1赞 user541686 7/29/2012
@bames53:真的吗?这有点微不足道......分配新容器,复制新数据(如果出现错误,则销毁副本),然后移动旧数据。它有效;它只是可能很慢,我想知道这是否得到保证。如果不是,那就不是。但就像我说的:我只是举了一个潜在的例子......如果我已经知道答案,那么我就不需要查找了!
1赞 user541686 7/29/2012
@bames53:促使我写这个问题的原因是,我只能找到标准中的和部分来回答我,这些部分本身非常不完整。例如,我什至没想到该部分会包含异常要求信息。所以是的,现在下面的答案就在这里,我可以看到很多信息都在那里,但是当我发布这个时,我找不到它。associative.reqmts.exceptunord.req.except.modifiers

答:

18赞 Johan Lundberg 7/28/2012 #1

阅读标准可能很可怕(让我们回到标准),但是Bjarne Stroustrup在他的书“C++编程语言”中写了一个非常好的附录。他将这个附录发布在

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

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

E.4 标准集装箱保证

如果库操作本身引发异常,它可以 - 并且确实 - 确保它所操作的对象保留在 明确定义的状态。例如,at() 抛出 out_of_range 向量 (§16.3.3) 不是向量异常安全的问题 .at() 的编写者可以毫无问题地确保向量位于 投掷前定义明确的状态。

此外,E.4.1节指出

除了基本保证之外,标准库还提供 对插入或删除元素的少数操作有很强的保证。

请看第 956 页。它包含矢量、deque、list 和 map 的各种操作的保证表。总之,这些容器上的所有操作要么是 nothrow 要么是 strong,除了 N - 元素插入到映射中,它提供了基本的保证。

注意:上面的文本很旧,没有涉及 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() 函数使任何引用、指针或 迭代器是指正在交换的容器的元素。

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

评论

1赞 user541686 7/28/2012
啊,我一开始没有意识到,但 PDF 似乎有我需要的信息(虽然我仍然需要检查)......不包含 C++11 信息,但仍然有用。:)谢谢。+1
0赞 user541686 7/28/2012
看起来它没有我需要的一切......例如,呢?是不是像?还是更强或更弱?(我意识到这在 C++11 中可能与 C++03 及更早版本不同——它可以使用复制构造函数、移动构造函数、复制赋值、移动赋值和交换的任意组合,具体取决于它的实现方式。我需要知道我可以依赖什么样的保证,最终对于 C++11 和 C++03,但 C++11 之前也是一个好的开始。std::rotatestd::copy
1赞 Johan Lundberg 7/28/2012
对于算法来说,这并不是那么清楚。我建议你列出你感兴趣的方法,并提出一个标题为“std算法的异常安全性”的新问题。这些算法使用 swap、insert 等方式运行,因此它们不能比底层容器做得更好,但您需要阅读标准中每个容器的要求。例如,对于旋转,容器元素需要是可交换的,等等。没有明确的声明(据我所知)关于保证,以防在轮换操作过程中抛出交换。我把它读成“弱”,但试着问一个新问题。
0赞 user541686 7/29/2012
好的,我想我会按照我在当前 STL 实现中看到的关于附录中不存在的内容,并以此为基础进行保证......如果需要,我稍后会提出更多问题。那个PDF很棒!
11赞 Martin York 7/28/2012 #2

货号 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() 函数会引发异常。erase(k) 不会引发异常,除非该异常是由容器的 Compare 对象(如果有)引发的。
2 对于关联容器,如果插入或放置函数中插入单个元素的任何操作引发异常,则插入不起作用。
3 对于关联容器,除非容器的 Compare 对象(如果有)的交换引发该异常,否则任何交换函数都不会引发异常。

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

23.2.5.1 例外安全保证 [unord.req.except]

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

23.3.3.4 deque 修饰符 [deque.modifiers]

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

备注:如果 T 的复制构造函数、移动构造函数、赋值运算符或移动赋值运算符以外的其他原因引发异常,则不会产生任何影响。如果非 CopyInsertable T 的移动构造函数引发异常,则未指定效果。

迭代器擦除(const_iterator first, const_iterator last);第6段

抛出:除非 T 的复制构造函数、移动构造函数、赋值运算符或移动赋值运算符引发异常,否则无任何内容。

23.3.6.5 矢量修饰符 [vector.modifiers]

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

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

迭代器擦除(const_iterator first, const_iterator last);第5段

抛出:除非 T 的复制构造函数、移动构造函数、赋值运算符或移动赋值运算符引发异常,否则无任何内容。

评论

0赞 user541686 7/28/2012
嗯。。。因此,例如,如果我调用在索引 9998 处的 10000 个元素的向量中插入 2 个元素,并且对象的复制构造函数可能会抛出,那么该向量是否会获取所有 10000 个元素并将它们复制到某个地方,以确保安全?vector::insert()
0赞 Martin York 7/28/2012
@Mehrdad:你错过了这个while inserting a **single element**
0赞 user541686 7/28/2012
呃,不,这正是我问你的原因。如果我插入一个元素,那么答案已经在您的帖子中了。我一直在寻找一些全面的东西——它可以解释会发生什么,例如,类似于上面的场景。
0赞 Martin York 7/28/2012
23.2.1 第10段:if an exception is thrown by an insert() or emplace() function while inserting a single element, that function has no effects.
1赞 Martin York 7/28/2012
@Mehrdad:你好像误解了我的回答。不保证 2(基本除外)。它仅在以下情况下提供保证。while inserting a **single element**
2赞 bames53 7/29/2012 #3

您链接到的文档(n3337 标准草案)可以被视为官方文档。这是 C++ 标准加上一些小的编辑更改。

你只需要学会阅读标准,这是可以理解的,因为它不是为了容易阅读。

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

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

评论

0赞 user541686 7/29/2012
你知道“修饰符”是什么意思吗?当我在那里看到例外保证时,我完全感到困惑......
0赞 bames53 7/29/2012
@Mehrdad 该标准将每个类的成员函数划分为不同的类别。如果您查看类概述,您将看到按类别划分的所有函数的列表。每个组的开头都有一个链接,该链接将带您进入指定该组中函数的部分。
0赞 bames53 7/29/2012
@Mehrdad 例如,第 23.3.6.0 节有向量类模板概述,如果向下滚动,您会看到注释“//23.3.6.3 capacity”,后跟与向量的大小和容量相关的函数。如果您点击第 23.3.6.3 节的链接,您将找到该组中每个函数的规范,包括有关函数提供的任何异常保证的信息。例如,为此指定“如果非 CopyInsertable 类型的移动构造函数引发异常,则不会产生任何影响。vector::reserve()
0赞 bames53 7/29/2012
@Mehrdad 换言之,“修饰符”部分是指“标准归类为修饰符的功能”。它与异常无关,您也可以在其他组中找到异常信息,例如“构造函数、副本和赋值”、“容量”、“数据”、“专用算法”等。
0赞 user541686 7/29/2012
哦。。。所以他们所说的“修饰符”只是指变异器?!这很有意义(除了如何和不是修饰符)......哇,这么的阻抗不匹配!感谢您的详细解释!resizeat