Title
Move assignment of containers
Status
cd1
Section
[container.requirements]
Submitter
Howard Hinnant

Created on 2007-05-05.00:00:00 last changed 164 months ago

Messages

Date: 2010-10-21.18:28:33

Proposed resolution:

Change [container.requirements]:

Table 89: Container requirements
expressionreturn typeoperational semantics assertion/note pre/post-conditioncomplexity
a = rv;X& All existing elements of a are either move assigned or destructed a shall be equal to the value that rv had before this construction (Note C) linear

Notes: the algorithms swap(), equal() and lexicographical_compare() are defined in clause 25. Those entries marked "(Note A)" should have constant complexity. Those entries marked "(Note B)" have constant complexity unless allocator_propagate_never<X::allocator_type>::value is true, in which case they have linear complexity. Those entries marked "(Note C)" have constant complexity if a.get_allocator() == rv.get_allocator() or if either allocator_propagate_on_move_assignment<X::allocator_type>::value is true or allocator_propagate_on_copy_assignment<X::allocator_type>::value is true and linear complexity otherwise.

Date: 2007-05-05.00:00:00

James Hopkin pointed out to me that if vector<T> move assignment is O(1) (just a swap) then containers such as vector<shared_ptr<ostream>> might have the wrong semantics under move assignment when the source is not truly an rvalue, but a moved-from lvalue (destructors could run late).

vector<shared_ptr<ostream>> v1;
vector<shared_ptr<ostream>> v2;
...
v1 = v2;               // #1
v1 = std::move(v2);    // #2

Move semantics means not caring what happens to the source (v2 in this example). It doesn't mean not caring what happens to the target (v1). In the above example both assignments should have the same effect on v1. Any non-shared ostream's v1 owns before the assignment should be closed, whether v1 is undergoing copy assignment or move assignment.

This implies that the semantics of move assignment of a generic container should be clear, swap instead of just swap. An alternative which could achieve the same effect would be to move assign each element. In either case, the complexity of move assignment needs to be relaxed to O(v1.size()).

The performance hit of this change is not nearly as drastic as it sounds. In practice, the target of a move assignment has always just been move constructed or move assigned from. Therefore under clear, swap semantics (in this common use case) we are still achieving O(1) complexity.

History
Date User Action Args
2010-10-21 18:28:33adminsetmessages: + msg3400
2007-05-05 00:00:00admincreate