vector :: insert的异常安全保证是什么?

时间:2013-10-23 03:52:02

标签: c++

我想知道,std::vector::insert的异常安全保障究竟是什么?我对这个函数的单参数和范围重载感兴趣。

3 个答案:

答案 0 :(得分:2)

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

  

如果除了复制构造函数之外抛出异常,移动构造函数,赋值运算符或移动赋值运算符T或任何InputIterator运算都没有效果。如果非CopyInsertable T的移动构造函数抛出异常,则效果未指定。

答案 1 :(得分:2)

通常,insert的单元素形式对[container.requirements.general] / 10的任何容器都有强烈的异常保证,但vector::insert是此规则的例外:< / p>

[vector.modifiers] / 1适用于vector::insert; InputIterator此处指的是插入范围的insert的重载。

  

如果除了复制构造函数之外抛出异常,移动构造函数,赋值运算符或移动赋值运算符T或任何InputIterator运算都没有效果。如果非CopyInsertable T的move-constructor抛出异常,则效果未指定。


vector是一个支持分配器的容器,这意味着它使用分配器进行内存分配构造其元素[container.requirements.general] / 3

  

对于受本子条款影响的声明allocator_type的组件,存储在这些组件中的对象应使用allocator_traits<allocator_type>::construct函数构造,并使用allocator_traits<allocator_type>::destroy函数销毁。这些函数仅针对容器的元素类型调用,而不是针对容器使用的内部类型调用。

我认为这意味着可以在不使用分配器的情况下创建不是容器元素的本地对象(例如,用于复制和交换)。否则,对价值类型的ctors的要求将毫无意义;分配器的construct函数可能具有与值类型的ctor不同的异常保证。


CopyInsertable在[container.requirements.general] / 13中指定,要求

allocator_traits<A>::construct(m, p, v);

结构良好;其中A是分配器类型,m的类型为Ap是指向T的指针,v是类型的表达式(constTT是容器的值类型。这是一个来自一个参数(复制或移动构造)的现场构造。

同样,MoveConstructible已指定,但v(始终)是T类型的右值。对于零个或多个参数,EmplaceConstructible遵循相同的形式,而不是v

序列容器的insert函数对其各种形式的值类型强加了不同的要求[sequence.reqmts];这里包括vector的其他要求:

  • 表示单个参数是类型(constT的左值,对于表单插入N个副本T应为{{ 1}}和CopyInsertable
  • 对于作为CopyAssignable类型的右值的单个参数,T应为TMoveInsertable
  • 对于范围形式,MoveAssignable应来自解除引用的迭代器(*); T;另外,如果范围的迭代器不是前向迭代器,则EmplaceConstructibleMoveInsertable

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

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


我将异常规范解释如下:

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

为什么需要将vector::insert和move-ctor(而不是CopyInsertable与rvalue相结合),我不知道。 move-ctor仅直接用于不是容器元素的对象(间接可能通过allocator::construct)。

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

对于强保证,移动和复制分配必须是不可用的,因为可能需要为construct移动元素;由于算法中创建的本地对象,copy-和move-ctor可能需要不受强保证。

答案 2 :(得分:1)

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

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

  

基本保证:保留组件的不变量,不泄漏任何资源。       强有力的保证:操作已成功完成或抛出异常,使程序状态与操作开始前完全一致。       无抛出保证:操作不会抛出异常。

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