向量::插入的异常安全保证是什么?
What is the exception safety guarantee of vector::insert?
我想知道,std::vector::insert
的异常安全保证到底是什么?我对这个函数的单参数和范围重载都感兴趣。
通常,根据 [container.requirements.general]/10,单元素形式的 insert
对任何容器都有很强的异常保证,但vector::insert
是此规则的例外:
[vector.modifiers]/1 适用于vector::insert
; 这里的InputIterator
是指插入范围的insert
的重载。
如果不是由复制构造函数、移动构造函数、赋值运算符或移动赋值运算符引发的,则
T
或任何InputIterator
操作引发异常,则不会产生任何影响。如果非CopyInsertable
T
的移动构造函数抛出异常,则未指定效果。
vector
是一个分配器感知容器,这意味着它使用分配器来分配内存和构造其元素 [container.requirements.general]/3
对于受此子句影响的声明
allocator_type
的组件,存储在这些组件中的对象应使用allocator_traits<allocator_type>::construct
函数构造并使用allocator_traits<allocator_type>::destroy
函数销毁。仅为容器的元素类型调用这些函数,而不为容器使用的内部类型调用这些函数。
我认为这意味着本地对象(不是容器的元素)可以在不使用分配器的情况下创建(例如用于复制和交换)。否则,对值类型的 ctor 的要求将毫无意义;分配器的 construct
函数可能具有与值类型的 CTOR 不同的异常保证。
CopyInsertable
在 [container.requirements.general]/13 中指定,要求
allocator_traits<A>::construct(m, p, v);
格式正确;其中A
是分配器类型,m
是A
类型,p
是指向T
的指针,v
是类型(const
)T
的表达式,T
是容器的值类型。这是一个参数的就地构造(复制或移动构造)。
同样,指定了 MoveConstructible
,但v
(始终)是类型 T
的右值。 对于零个或多个参数,EmplaceConstructible
遵循相同的形式,而不是v
。
序列容器的insert
函数对其各种形式的值类型施加了不同的要求[sequence.reqmts];这里包括对vector
的其他要求:
- 对于类型
- 为(
const
T
)的左值的单个参数,对于插入N个副本的形式,T
应CopyInsertable
并CopyAssignable
对于类型为 T
的右值的单个参数,T
应MoveInsertable
并MoveAssignable
- 对于范围形式,应从取消引用的迭代器 (*) 中
EmplaceConstructible
T
;此外,如果范围的迭代器不是前向迭代器,则MoveInsertable
和MoveAssignable
(*) 注意:如果必须调整容器的大小以进行插入(例如,此类迭代器的*i
不是值类型),则仅从取消引用的迭代器中EmplaceConstructible
是不够的。规范可能要求范围形式继承单元素形式的要求,即MoveAssignable
或CopyAssignable
。
附带备注:insert
的范围形式要求两个迭代器不指向要插入它们的容器。
我将异常规范解释如下:
vector::insert
的异常规范中关于CopyInsertable
的附加语句可能区分了基本保证和无保证:容器的 dtor 通常需要调用所有元素的 dtor 并释放所有内存(在一般容器需求中)。也就是说,除非行为未指定/未定义,否则基本保证成立。
为什么有将CopyInsertable
和移动 ctor 结合起来的要求(而不是用右值allocator::construct
),我不知道。move-ctor 仅直接用于不是容器元素的对象(间接可能通过 allocator::construct
)。
关于"无影响"(->强保证)的其他注释不适用于分配器操作(construct
)。分配器操作显然不必是例外。由于您可以提供一个非默认分配器,该分配器不使用值类型的复制和移动 ctor 进行construct
,因此即使对于construct
分配器操作,insert
也必须提供强大的异常保证。例如,如果分配器的construct
不是 noex,但在调整大小期间,元素不能移动构造,而必须复制。
和复制赋值必须为"无",因为元素可能需要移动insert
;复制和移动 ctor 可能需要为"否",因为算法中创建了本地对象,因此需要"强保证"。
确切的保证在 C++11 23.3.6.5 中给出:
如果引发异常不是由复制构造函数、移动构造函数、赋值运算符或移动赋值运算符
T
或任何InputIterator
操作引发的,则不会产生任何影响。如果非CopyInsertable
T
的移动构造函数抛出异常,则未指定效果。
如果 insert
方法在列表末尾插入单个元素并且不需要分配任何内存,则提供强大的异常保证。
添加多个元素,或者必须分配内存,它提供了一个基本的异常保证。 Boost对异常保证有很好的描述。
基本保证:保留组件的不变量,不泄露任何资源。 强有力的保证:操作已成功完成或引发异常,使程序状态与操作开始之前完全相同。 无抛出保证:操作不会引发异常。
这意味着在异常之后,您知道该vector
将可用,但它可能没有您插入的所有数据。 所有成功插入的对象都将被完全构造。
- 是什么导致了Unity 3D中的"错误线程异常"?
- C++异常被捕获延迟,可能导致这种情况的原因是什么?
- C++ 捕获异常后进行清理的标准方法是什么?
- 处理许多自定义异常的最佳方法是什么
- 提出异常并处理C 的某些异常类型的正确方法是什么?
- 在类中抛出异常的最佳方法是什么
- 是什么导致异常输出以及如何修复
- 与Java的InputMismatchException和IOException Handling等效的C++异常处理机制是什么
- 在C++中使用异常的可能的错误处理策略是什么,它们的后果和影响是什么
- 在使用SFML时,我会得到记忆或出于界限异常,这是什么问题
- C++ 异常中的 catch(异常)是什么意思?
- 用作类型或异常规范时"see below"是什么意思?
- procdump.exe显示的异常的含义是什么
- 复制构造函数中出现异常的可能原因是什么
- 控件的CWnd::DefWindowProc上发生Stackloverlow异常的原因是什么
- 异常代码"EXC_I386_GPFLT"的含义是什么?
- 向量::插入的异常安全保证是什么?
- 抛出的(默认)构造函数中的异常保证应该是什么
- C++11 中默认虚拟析构函数的异常规范是什么?
- C++ 我不明白我的异常是什么()行为