阅读标准文档可能让人望而生畏(我们先回到标准上),但Bjarne Stroustrup在他的书《C++程序设计语言》附录中写了一篇关于此主题的非常好的文章。他在以下网址发布了这篇附录:
http://www.stroustrup.com/3rd_safe0.html ,以及 http://www.stroustrup.com/3rd_safe.pdf
这篇文章相当长且详细(并且写得很好)。例如,您可能会对E.4章节感兴趣,引用如下:
E.4 标准容器保证
如果库操作本身抛出异常,它可以 - 也确实 - 确保其操作对象被留在一个定义良好的状态。例如,对于向量(§16.3.3)抛出out_of_range的at()不是向量的异常安全性问题。at()的编写者没有任何问题,在抛出异常之前确保向量处于定义良好的状态。
此外,E.4.1章节指出:
除了基本保证外,标准库还针对一些插入或删除元素的操作提供了强异常安全保证。
请参阅第956页。其中包含了关于向量、双端队列、列表和映射的各种操作保证的表格。 总之,这些容器上的所有操作要么不抛出异常(nothrow),要么提供强异常安全保证,除了N - 元素插入到映射只提供基本保证。
注意:上述文本是旧的,不适用于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()函数不会使引用、指针或迭代器无效,这些引用、指针或迭代器是指向被交换容器的元素的。
上述各节中指出的问题(每个称为异常安全性保证)主要涉及特殊情况,如处理包含类型的哈希、比较操作以及抛出交换和抛出移动操作时的异常。
std::rotate
呢?它像std::copy
吗?还是更强或更弱?(我意识到这在C++11和之前的C++03中可能有所不同——它可以使用任何组合的复制构造函数、移动构造函数、复制赋值函数、移动赋值函数和交换函数,具体取决于它是如何实现的。我需要知道它可以提供什么样的保证,无论是对于C++11还是C++03,但以C++11之前的版本为起点也是一个好的开始。) - user541686除非另有规定(参见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()迭代器不引用任何元素,因此可能会失效。——注释]
1 对于关联容器,没有clear()函数会抛出异常。除非由容器的比较对象(如果有)抛出异常,否则erase(k)不会抛出异常。
2 对于关联容器,如果在插入单个元素时从insert或emplace函数内部抛出异常,则插入没有效果。
3 对于关联容器,除非容器的比较对象(如果有)的交换引发异常,否则任何swap函数都不会引发异常。
1 对于无序关联容器,没有clear()函数会抛出异常。除非由容器的哈希或Pred对象(如果有)抛出异常,否则erase(k)不会抛出异常。
2 对于无序关联容器,如果从insert或emplace函数内部除容器的哈希函数以外的任何操作抛出异常,则插入没有效果。
3 对于无序关联容器,除非容器的Hash或Pred对象(如果有)的交换引发异常,否则任何swap函数都不会引发异常。
4 对于无序关联容器,如果在rehash()函数内部除了由容器的哈希函数或比较函数引发的异常之外还有其他异常,则rehash()函数没有效果。
注意:如果T的复制构造函数、移动构造函数、赋值运算符或移动赋值运算符以外的异常被抛出,则没有任何影响。如果非CopyInsertable T的移动构造函数抛出异常,则效果是未指定的。
除非T的复制构造函数、移动构造函数、赋值运算符或移动赋值运算符抛出异常,否则不会抛出任何异常。
如果非CopyInsertable T的移动构造函数抛出异常,则效果是未指定的。
除非T的复制构造函数、移动构造函数、赋值运算符或移动赋值运算符抛出异常,否则不会抛出任何异常。
vector::insert()
在10000个元素的向量中的索引9998处插入2个元素,并且对象的复制构造函数可能会抛出异常,那么向量是否会将所有10000个元素复制到其他地方以确保安全? - user541686vector::reserve()
,指定“如果抛出除非非CopyInsertable类型的移动构造函数之外的异常,则没有效果。” - bames53resize
和at
不是modifiers之外)...哇,这样的阻抗不匹配!感谢详细的解释! - user541686
vector::insert
(底层可能使用std::swap
)。不管怎样,我并没有说这是保证的,我只是举了一个我正在寻找的潜在示例。 - user541686associative.reqmts.except
和unord.req.except
部分找到答案,它们本身非常不完整。我甚至从未想过.modifiers
部分会包含异常要求信息。所以是的,现在有了下面的答案,我可以看到很多信息都在那里,但当我发布这篇文章时,我找不到它。 - user541686