简体   繁体   中英

std::vector::erase exception safety

I have read that std::vector erase method use move operations only if type is known to not emit exceptions due to strong exception safety. Other comments are that erase method guarantee basic or no throw exception safety depending on that if element constructor throws or not. I wasn't able to clarify that in my C++11 draft. I did test and it shows basic exception safety guarantee, it also used move constructor which was not marked as noexcept. Did I overlook something ? What is right ?

Table 100 -- Sequence container requirements in section 23.2.3 [sequence.reqmts] says:

a.erase(q)

Requires: For vector and deque , T shall be MoveAssignable .

This means that the implementation can call no operations on T except to destruct it or move assign it. Note that if the implementation move assigns T that doesn't guarantee that a move assignment operator will be called. For example T may not have a move assignment operator, and so in that case a copy assignment operator could be called. However the implementation is not allowed to copy assign the T , only move assign it.

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

Furthermore 23.3.6.5 vector modifiers [vector.modifiers] says the following:

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 .

I must admit I sighed when I read this. There is clearly a minor defect here. This operation is not allowed to form any expressions that would directly construct a T . Perhaps one is constructed as an implementation detail inside an assignment operator of T , but that is of no concern to this specification. The concern is does this expression throw or not:

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

If that expression (where i and j are iterators referring to a T ) does not throw, then vector::erase has the no-throw guarantee. Otherwise vector::erase has the basic exception safety guarantee.

Note that for this operation, the implementation is not allowed to fall back to copy assignment if is_nothrow_move_assignable<T>::value is false. Such logic is present in other vector operations such as push_back , but not here.

Also note the Complexity specification of this same section:

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.

Restated: If you erase a range of elements that end with the end of the vector, zero move assignments will be performed, and move assignment is the only thing that might throw. So you get back the no-throw guarantee if you are erasing at the end, even if is_nothrow_move_assignable<T>::value is false.

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.

Provided your implementation conforms to this, it may implement the erase as it sees fit. There is no implicit exception safety guarantee as far as I can tell.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM