std::vector::擦除异常安全

std::vector::erase exception safety

本文关键字:安全 异常 vector std 擦除      更新时间:2023-10-16

我读到std::vector era方法仅在已知类型由于强大的异常安全性而不发出异常时才使用移动操作。其他评论是擦除方法保证基本或无抛出异常安全性,具体取决于元素构造函数是否抛出。我无法在我的C++11草案中澄清这一点。我做了测试,它显示了基本的异常安全保证,它还使用了未标记为noexcept的移动构造函数。我忽略了什么吗?什么是对的?

表 100 -- 第 23.2.3 节 [sequence.reqmts] 中的序列容器要求说:

a.erase(q)

要求:对于vectordequeTMoveAssignable

这意味着实现不能对T调用任何操作,除非销毁它或移动分配它。 请注意,如果实现移动分配T并不能保证将调用移动分配运算符。 例如,T可能没有移动赋值运算符,因此在这种情况下可以调用复制赋值运算符。 但是,该实现不允许复制分配T,只能移动分配它。

*i = std::move(*j);  // implementation can do this
*i = *j;             // implementation can not do this

此外,23.3.6.5 矢量修饰符 [vector.modifiers] 表示如下:

iterator erase(const_iterator position);
iterator erase(const_iterator first, const_iterator last);
抛出

:除非复制构造函数、移动构造函数、赋值运算符或移动赋值引发异常,否则不抛出任何内容 T的运营商 .

必须承认,当我读到这里时,我叹了口气。 这里显然有一个小缺陷。 不允许此操作形成任何直接构造T的表达式。 也许一个被构造为T赋值运算符中的实现细节,但这与这个规范无关。 问题是这个表达式是否抛出:

*i = std::move(*j);  // implementation can do this. Will it throw?

如果该表达式(其中ij是引用T的迭代器)不抛出,则vector::erase具有不抛出保证。 否则vector::erase具有基本的异常安全保证。

请注意,对于此操作,如果 is_nothrow_move_assignable<T>::value为 false,则不允许实现回退到复制分配。 这种逻辑存在于其他vector操作中,例如 push_back ,但在这里不存在。

另请注意同一部分的复杂性规范:

复杂性:T的析构函数称为等于擦除元素数的次数,但移动赋值 T运算符称为等于 元素在擦除的元素之后的矢量中。

重述:如果擦除一系列以向量结尾结尾的元素,将执行移动分配,并且移动分配是唯一可能抛出的内容。 因此,如果您在最后擦除,即使is_nothrow_move_assignable<T>::value是假的,您也会得到不扔的保证。

23.3.6.5 抛出:除非 T 的复制构造函数、移动构造函数、赋值运算符或移动赋值运算符抛出异常,否则不抛出任何异常。

如果您的实现符合此要求,它可能会根据需要实现擦除。据我所知,没有隐含的异常安全保证。