【发布时间】:2013-11-01 04:24:53
【问题描述】:
我想知道,std::vector::insert 的异常安全保证到底是什么?我对这个函数的单参数和范围重载都感兴趣。
【问题讨论】:
-
这很大程度上取决于容器中存储的类型
-
@David:你是对的。我对它没有 nothrow 移动的情况感兴趣 - 例如只扔副本。
标签: c++
我想知道,std::vector::insert 的异常安全保证到底是什么?我对这个函数的单参数和范围重载都感兴趣。
【问题讨论】:
标签: c++
一般来说,insert 的单元素形式对任何容器都有很强的例外保证,根据 [container.requirements.general]/10,但vector::insert 是此规则的一个例外:
[vector.modifiers]/1 适用于vector::insert;这里的InputIterator 指的是插入范围的insert 的重载。
如果除了
T的复制构造函数、移动构造函数、赋值运算符或移动赋值运算符或任何InputIterator操作引发异常,则没有任何影响。如果非CopyInsertableT的移动构造函数抛出异常,则效果未指定。
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和@987654346的表达式@ 是容器的值类型。这是一个参数的就地构造(复制或移动构造)。
同样,MoveConstructible 被指定,但v (始终)是T 类型的右值。 EmplaceConstructible 对零个或多个参数使用相同的形式,而不是 v。
序列容器的insert函数对其各种形式的值类型提出了不同的要求[sequence.reqmts];这里包括对vector 的额外要求:
const) T 的左值的单个参数以及 插入 N 个副本 的形式,T 应为 CopyInsertable 和 CopyAssignable
T 类型右值的单个参数,T 应为MoveInsertable 和MoveAssignable
T 应为来自取消引用的迭代器 (*) 的 EmplaceConstructible;此外,MoveInsertable 和 MoveAssignable 如果范围的迭代器不是前向迭代器(*) 注意:EmplaceConstructible 仅来自取消引用的迭代器是不够的,如果容器必须调整大小以进行插入(例如,此类迭代器的 *i 不是值类型)。规范可能要求范围形式继承单元素形式的要求,即MoveAssignable 或CopyAssignable。
旁注:insert 的范围形式要求两个迭代器不指向要插入它们的容器。
我将异常规范解释如下:
vector::insert的异常规范中关于CopyInsertable的附加声明大概区分了基本保证和不保证:容器的dtor一般要求调用所有元素的dtor并释放所有内存(在一般容器要求)。也就是说,除非行为未指定/未定义,否则基本保证成立。
我不知道为什么需要结合 CopyInsertable 和 move-ctor(而不是 allocator::construct 和右值)。 move-ctor 仅直接用于不是容器元素的对象(可能通过allocator::construct 间接使用)。
关于“无影响”(-> 强保证)的其他评论不应用于分配器操作 (construct)。分配器操作显然不必是 noexcept。因为您可以为construct 提供不使用值类型的复制和移动ctor 的非默认分配器,所以insert 必须提供强大的异常保证,即使对于construct 分配器操作也是如此。例如,如果分配器的 construct 不是 noexcept,则在调整大小期间,元素不能移动构造,但必须复制。
移动和复制分配必须是除了强有力的保证之外,因为元素可能需要为insert 移动;由于算法中创建了本地对象,copy- 和 move-ctor 可能需要为 noexcept 以提供强保证。
【讨论】:
确切的保证在 C++11 23.3.6.5 中给出:
如果除了
T的复制构造函数、移动构造函数、赋值运算符或移动赋值运算符或任何InputIterator操作引发异常,则没有任何影响。如果非CopyInsertableT的移动构造函数抛出异常,则效果未指定。
【讨论】:
there are no effects 是什么意思?向量中已分配的元素会发生什么情况?
T的复制构造函数、移动构造函数、赋值运算符或移动赋值运算符或任何InputIterator操作”引发的,在这种情况下没有这样的保证.