13

我想知道,异常安全保证到底是什么std::vector::insert?我对此函数的单参数和范围重载都感兴趣。

4

3 回答 3

4

通常,根据 [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是类型Ap是指向的指针Tv是类型 ( const)的表达式TT是容器的值类型。这是来自一个参数的就地构造(复制或移动构造)。

类似地,MoveConstructible被指定,但v(总是)是类型的右值TEmplaceConstructible对零个或多个参数遵循相同的形式,而不是v.

序列容器的insert功能对其各种形式的值类型提出了不同的要求[sequence.reqmts];这里包括额外的要求vector

  • const对于一个类型为 ( )的左值的单个参数,对于插入 N 个T的形式,应为TCopyInsertableCopyAssignable
  • 对于类型为 rvalue 的单个参数TT应为MoveInsertableMoveAssignable
  • 对于范围形式,TEmplaceConstructible来自取消引用的迭代器 (*);另外,MoveInsertable如果MoveAssignable范围的迭代器不是前向迭代器

(*) 注意:EmplaceConstructible如果容器必须为插入而调整大小(例如,*i对于这样的迭代器不是值类型),则仅来自取消引用的迭代器是不够的。规范可能要求范围形式继承单元素形式的要求,即要么MoveAssignable要么CopyAssignable

旁注:范围形式insert要求两个迭代器不指向要插入它们的容器。


我将异常规范解释如下:

CopyInsertable异常规范中关于vector::insert可能区分基本保证和不保证的附加声明:容器的dtor通常需要调用所有元素的dtor并释放所有内存(在一般容器要求中)。也就是说,除非行为未指定/未定义,否则基本保证成立。

我不知道为什么需要结合CopyInsertable和 move-ctor (而不是右值)的要求。allocator::constructmove-ctor 仅直接用于不是容器元素的对象(间接可能通过allocator::construct)。

关于“无影响”(-> 强保证)的其他评论不适用于分配器操作(construct)。分配器操作显然不必是 noexcept。由于您可以提供不使用值类型的复制和移动ctor 的非默认分配器,因此construct即使insert对于construct分配器操作也必须提供强大的异常保证。例如,如果分配器construct不是 noexcept,则在调整大小期间,元素不能被移动构造,但必须被复制。

移动和复制分配必须是除了强有力的保证之外,因为元素可能需要为insert;移动。由于算法中创建了本地对象,copy- 和 move-ctor 可能需要为 noexcept 以提供强保证。

于 2013-10-23T07:54:51.697 回答
3

确切的保证在 C++11 23.3.6.5 中给出:

如果除复制构造函数、移动构造函数、赋值运算符或移动赋值运算符之外的T任何InputIterator操作引发异常,则不会产生任何影响。如果 non- 的移动构造函数抛出异常CopyInsertable T,则未指定效果。

于 2013-10-23T07:21:26.023 回答
1

如果该insert方法在列表末尾插入单个元素并且不需要分配任何内存,则提供强大的异常保证。

如果它必须添加多个元素,或者必须分配内存,它提供了基本的异常保证。 Boost对异常保证有很好的描述。

基本保证:保留组件的不变量,不泄露任何资源。强有力的保证:操作要么成功完成,要么抛出异常,使程序状态完全保持在操作开始之前的状态。不抛出保证:操作不会抛出异常。

这意味着在发生异常后,您知道该vector将可用,但它可能没有您插入的所有数据。所有成功插入的对象都将被完全构造。

于 2013-10-23T03:58:44.717 回答