std::vector::erase 异常安全

mbo*_*ws2 1 c++ vector c++11

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

How*_*ant 6

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

a.erase(q)
Run Code Online (Sandbox Code Playgroud)

要求:对于vectordequeT应为MoveAssignable

这意味着T除了破坏它或移动分配它之外,实现不能调用任何操作。请注意,如果实现移动分配T,并不能保证将调用移动分配运算符。例如,T可能没有移动赋值运算符,因此在这种情况下可以调用复制赋值运算符。然而,实现不允许复制分配T,只能移动分配。

*i = std::move(*j);  // implementation can do this
*i = *j;             // implementation can not do this
Run Code Online (Sandbox Code Playgroud)

此外,23.3.6.5 向量修饰符 [vector.modifiers] 说明如下:

iterator erase(const_iterator position);
iterator erase(const_iterator first, const_iterator last);
Run Code Online (Sandbox Code Playgroud)

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

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

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

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

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

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

复杂性:的析构函数T被调用的次数等于被擦除的元素的个数,但是 的移动赋值运算符T被调用的次数等于被擦除的元素之后的向量中的元素的个数。

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