std::vector::erase 异常安全
std::vector::erase exception safety
我读到 std::vector 擦除方法仅在已知类型由于强异常安全性而不会发出异常时才使用移动操作。其他评论是擦除方法保证基本或不抛出异常安全取决于元素构造函数是否抛出。我无法在我的 C++11 草稿中澄清这一点。我做了测试,它显示了基本的异常安全保证,它还使用了未标记为 noexcept 的移动构造函数。我忽略了什么吗?什么是对的 ?
23.3.6.5 Throws: Nothing unless an exception is thrown by the copy constructor, move constructor, assignment operator, or move assignment operator of T.
只要您的实施符合此要求,它就可以实施它认为合适的擦除。据我所知,没有隐式的异常安全保证。
Table 100 -- 第 23.2.3 节中的序列容器要求 [sequence.reqmts] 说:
a.erase(q)
Requires: For vector
and deque
, T
shall be MoveAssignable
.
这意味着该实现不能在 T
上调用任何操作,除非对其进行析构或移动分配。请注意,如果实现 move assigns T
并不能保证将调用 move 赋值运算符。例如 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);
Throws: Nothing unless an exception is thrown by the copy constructor, move constructor, assignment operator, or move assignment
operator of T
.
我必须承认,当我读到这里时,我叹了口气。这里显然有一个小缺陷。此操作不允许形成任何直接构造 T
的表达式。也许一个被构造为 T
的赋值运算符内部的实现细节,但这与本规范无关。问题是这个表达式是否抛出:
*i = std::move(*j); // implementation can do this. Will it throw?
如果该表达式(其中 i
和 j
是引用 T
的迭代器)不抛出,则 vector::erase
具有不抛出保证。否则vector::erase
有基本的异常安全保障。
请注意,对于此操作,如果 is_nothrow_move_assignable<T>::value
为假,则不允许执行回退到复制分配。这种逻辑存在于其他 vector
操作中,例如 push_back
,但这里没有。
另请注意同一部分的复杂性规范:
Complexity: The destructor of T
is called the number of times equal to the number of the elements erased, but the move assignment
operator of T
is called the number of times equal to the number of
elements in the vector after the erased elements.
重述:如果擦除以向量末尾结尾的一系列元素,将执行 零 移动赋值,移动赋值是唯一可能抛出的东西.因此,如果您在最后擦除,即使 is_nothrow_move_assignable<T>::value
为假,您也会获得不抛出保证。
我读到 std::vector 擦除方法仅在已知类型由于强异常安全性而不会发出异常时才使用移动操作。其他评论是擦除方法保证基本或不抛出异常安全取决于元素构造函数是否抛出。我无法在我的 C++11 草稿中澄清这一点。我做了测试,它显示了基本的异常安全保证,它还使用了未标记为 noexcept 的移动构造函数。我忽略了什么吗?什么是对的 ?
23.3.6.5 Throws: Nothing unless an exception is thrown by the copy constructor, move constructor, assignment operator, or move assignment operator of T.
只要您的实施符合此要求,它就可以实施它认为合适的擦除。据我所知,没有隐式的异常安全保证。
Table 100 -- 第 23.2.3 节中的序列容器要求 [sequence.reqmts] 说:
a.erase(q)
Requires: For
vector
anddeque
,T
shall beMoveAssignable
.
这意味着该实现不能在 T
上调用任何操作,除非对其进行析构或移动分配。请注意,如果实现 move assigns T
并不能保证将调用 move 赋值运算符。例如 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);
Throws: Nothing unless an exception is thrown by the copy constructor, move constructor, assignment operator, or move assignment operator of
T
.
我必须承认,当我读到这里时,我叹了口气。这里显然有一个小缺陷。此操作不允许形成任何直接构造 T
的表达式。也许一个被构造为 T
的赋值运算符内部的实现细节,但这与本规范无关。问题是这个表达式是否抛出:
*i = std::move(*j); // implementation can do this. Will it throw?
如果该表达式(其中 i
和 j
是引用 T
的迭代器)不抛出,则 vector::erase
具有不抛出保证。否则vector::erase
有基本的异常安全保障。
请注意,对于此操作,如果 is_nothrow_move_assignable<T>::value
为假,则不允许执行回退到复制分配。这种逻辑存在于其他 vector
操作中,例如 push_back
,但这里没有。
另请注意同一部分的复杂性规范:
Complexity: The destructor of
T
is called the number of times equal to the number of the elements erased, but the move assignment operator ofT
is called the number of times equal to the number of elements in the vector after the erased elements.
重述:如果擦除以向量末尾结尾的一系列元素,将执行 零 移动赋值,移动赋值是唯一可能抛出的东西.因此,如果您在最后擦除,即使 is_nothrow_move_assignable<T>::value
为假,您也会获得不抛出保证。