截至N3797版本,C++标准要求容器的swap
函数不抛出异常,除非特别指定[container.requirements.general]
(23.2.1§10)。
- 为什么被指定不抛出异常的
swap
成员函数没有声明为noexcept
?
同样的问题也适用于专门的非成员swap
重载。
截至N3797版本,C++标准要求容器的swap
函数不抛出异常,除非特别指定[container.requirements.general]
(23.2.1§10)。
swap
成员函数没有声明为noexcept
?同样的问题也适用于专门的非成员swap
重载。
在refp所说的基础上,以下是来自Daniel Krügler在std-discussion邮件列表上的文章:
将函数声明为无条件noexcept的内部策略解释在
http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2011/n3279.pdf
在该文中使用的术语,std::vector的swap函数具有收缩合同,即它对参与对象的分配器有前提条件。这意味着调用者可能会违反前提条件,并且实现应允许通过不同于终止的方式来发出信号。因此,这样的函数不应该是noexcept,但是它应该具有一个有效的元素 "Throws: Nothing",因为这适用于满足前提条件的情况。
(链接)
所述的内部政策是您问题的规范和官方答案。
swap()
函数不会抛出异常,但它仍然抛出异常(因为前置条件被违反),那么异常安全和不变量保持都将无从谈起。程序最好直接终止。 - Ralph Tandetzky起初听起来可能有些奇怪,但不明确说明标准容器的swap
是否为noexcept
是有意的;这一切都归结于未定义行为(UB)。
23.2.1p9
通用容器要求[container.requirements.general]
对于除了
array
以外的标准容器类型a
和b
,表达式a.swap(b)
将交换a
和b
的值,而不会在各个容器元素上调用任何移动、复制或交换操作。属于
a
和b
的任何Compare
、Pred
或Hash
对象都应该是可交换的,并且应该通过非成员swap
进行交换。如果
allocator_traits<allocator_type>::propagate_on_container_swap::value
为true
,则a
和b
的分配器也应该通过非成员swap
进行交换。否则,除非`a.get_allocator() == b.get_allocator()`,否则它们不应该被交换,行为未定义。
注意:由我添加的斜体字。
为什么前面的部分与我的问题相关?
由于标准容器的swap
具有前提条件(最重要的是标准先前引用的最后一段),如果不满足可能会导致UB,因此标准不想对实现施加“不可能”的约束。
只有罪犯和销售员可能认为“不”是其他东西,但当标准说“没有要求”时,它确实意味着“没有要求”;将相关的swap
函数标记为noexcept
会对实现施加要求,而实际上应该没有要求。
为什么标准不想强制执行这些要求?
关于这个问题,有一篇有趣的论文(N3248),作者是Alisdair Meredith和John Lakos,题为"noexcept
prevents Library Validation"。
简而言之,它讲述了如何使用noexcept
将阻止库实现在库代码(即标准库的实现)中使用asserts
,即使在调试模式下也是如此,并且这样做的影响。
如果C++有一个标准化的"测试" vs "生产"模式(正如论文所称),其中noexcept
会有条件地应用,那么这将大大减少问题。但目前情况是:C++没有"模式"。