我想知道,异常安全保证到底是什么std::vector::insert
?我对此函数的单参数和范围重载都感兴趣。
3 回答
通常,根据 [container.requirements.general]/10,单元素形式的insert
对任何容器都有很强的例外保证,但vector::insert
该规则是一个例外:
[vector.modifiers]/1 适用于vector::insert
; InputIterator
这里指的insert
是插入范围的重载。
如果除复制构造函数、移动构造函数、赋值运算符或移动赋值运算符之外的
T
任何InputIterator
操作引发异常,则没有任何影响。如果 non- 的移动构造函数抛出异常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
对于一个类型为 ( )的左值的单个参数,对于插入 N 个T
的形式,应为T
CopyInsertable
CopyAssignable
- 对于类型为 rvalue 的单个参数
T
,T
应为MoveInsertable
和MoveAssignable
- 对于范围形式,
T
应EmplaceConstructible
来自取消引用的迭代器 (*);另外,MoveInsertable
如果MoveAssignable
范围的迭代器不是前向迭代器
(*) 注意:EmplaceConstructible
如果容器必须为插入而调整大小(例如,*i
对于这样的迭代器不是值类型),则仅来自取消引用的迭代器是不够的。规范可能要求范围形式继承单元素形式的要求,即要么MoveAssignable
要么CopyAssignable
。
旁注:范围形式insert
要求两个迭代器不指向要插入它们的容器。
我将异常规范解释如下:
CopyInsertable
异常规范中关于vector::insert
可能区分基本保证和不保证的附加声明:容器的dtor通常需要调用所有元素的dtor并释放所有内存(在一般容器要求中)。也就是说,除非行为未指定/未定义,否则基本保证成立。
我不知道为什么需要结合CopyInsertable
和 move-ctor (而不是右值)的要求。allocator::construct
move-ctor 仅直接用于不是容器元素的对象(间接可能通过allocator::construct
)。
关于“无影响”(-> 强保证)的其他评论不适用于分配器操作(construct
)。分配器操作显然不必是 noexcept。由于您可以提供不使用值类型的复制和移动ctor 的非默认分配器,因此construct
即使insert
对于construct
分配器操作也必须提供强大的异常保证。例如,如果分配器construct
不是 noexcept,则在调整大小期间,元素不能被移动构造,但必须被复制。
移动和复制分配必须是除了强有力的保证之外,因为元素可能需要为insert
;移动。由于算法中创建了本地对象,copy- 和 move-ctor 可能需要为 noexcept 以提供强保证。
确切的保证在 C++11 23.3.6.5 中给出:
如果除复制构造函数、移动构造函数、赋值运算符或移动赋值运算符之外的
T
任何InputIterator
操作引发异常,则不会产生任何影响。如果 non- 的移动构造函数抛出异常CopyInsertable
T
,则未指定效果。