This page is a snapshot from the LWG issues list, see the Library Active Issues List for more information and the meaning of New status.
Section: 23.3.5.4 [deque.modifiers], 23.3.13.5 [vector.modifiers], 23.3.16.5 [inplace.vector.modifiers] Status: New Submitter: Jonathan Wakely Opened: 2024-07-25 Last modified: 2024-12-06
Priority: 3
View other active issues in [deque.modifiers].
View all other issues in [deque.modifiers].
View all issues with New status.
Discussion:
The spec for deque::erase talks about a exception
"thrown by the assignment operator of T" but it's unclear which
assignment operator this means.
Arguably, this is fine because it means "the assignment operator that
is used when repositioning the remaining elements".
However, deque::append_range, vector::append_range, vector::erase,
inplace_vector::append_range, and inplace_vector::erase talk about
"the assignment operator or move assignment operator" which is just odd.
In C++03 this just said "the assignment operator" and move semantics
added "or the move assignment operator" but we could improve it.
What we should talk about is "an assignment operator", or "the assignment operator selected by overload resolution for the assignment expressions performed by the operation", or something like that.
This is potentially a bigger issue than just assignment:
for append_range we say
"If an exception is thrown other than by the copy constructor,
move constructor, assignment operator, or move assignment operator [...]"
and there's no guarantee that the constructor used for initializing a
Cpp17CopyInsertable type is a copy constructor or move constructor.
It could be some templated constructor that is a better match than any
of the special member functions.
[2024-08-02; Reflector poll]
Set priority to 3 after reflector poll. Arthur to draft wording.
[2024-12-06; LWG telecon]
23.3.11.4 [list.modifiers] p1 says:
Complexity: Insertion of a single element into a list takes constant time and exactly one call to a constructor ofIn addition to incorrectly talking about "the copy constructor or move constructor", it should not should not talk about any "call to a constructor" because scalars do not have constructors at all. We should talk about calls toT. Insertion of multiple elements into a list is linear in the number of elements inserted, and the number of calls to the copy constructor or move constructor ofTis exactly equal to the number of elements inserted.
allocator_traits::construct not constructors,
or objects being constructed.
Similarly, p5 says:
Complexity: Erasing a single element is a constant time operation with a single call to the destructor ofThis should talk about calls toT. Erasing a range in a list is linear time in the size of the range and the number of calls to the destructor of typeTis exactly equal to the size of the range.
allocator_traits::destroy,
or objects being destroyed.
23.3.5.4 [deque.modifiers] is similar. Look for similar problems elsewhere.
[2024-12-06; Jonathan adds wording, incorporating Arthur's wording]
Proposed resolution:
This wording is relative to N4993.
Modify 23.3.5.3 [deque.capacity] as indicated:
void shrink_to_fit();-5- Preconditions:
Tis CppMoveInsertable intodeque.-6- Effects:
shrink_to_fitis a non-binding request to reduce memory use but does not change the size of the sequence. [Note 1: The request is non-binding to allow latitude for implementation-specific optimizations. — end note] If the size is equal to the old capacity, or if an exception is thrown other than by themove constructormove-construction of one object of a non-Cpp17CopyInsertable typeTfrom another, then there are no effects.
Modify 23.3.5.4 [deque.modifiers] as indicated:
iterator insert(const_iterator position, const T& x); ... template<container-compatible-range<T> R> void append_range(R&& rg);-1- Effects: [...]
-2- Complexity: The complexity is linear in the number of elements inserted plus the lesser of the distances to the beginning and end of the deque. Inserting a single element at either the beginning or end of a deque always takes constant time and
causes a single call to a constructor of Tconstructs a single object of typeT.-3- Remarks: If an exception is thrown other than by the
copy constructor, move constructor, assignment operator, or move assignment operator ofconstruction or assignment of one object of typeTTfrom another , there are no effects. If an exception is thrown while inserting a single element at either end, there are no effects. Otherwise, if an exception is thrown by themove constructor of amove-construction of one object of non-Cpp17CopyInsertable typeTfrom another, the effects are unspecified.[...]
-5- Throws: Nothing unless an exception is thrown bythe assignment operator ofthe assignment of one object of typeTTfrom another .iterator erase(const_iterator position); iterator erase(const_iterator first, const_iterator last); void pop_front(); void pop_back();-4- Effects: An erase operation that erases the last element of a deque invalidates only the past-the-end iterator and all iterators and references to the erased elements. An erase operation that erases the first element of a deque but not the last element invalidates only iterators and references to the erased elements. An erase operation that erases neither the first element nor the last element of a deque invalidates the past-the-end iterator and all iterators and references to all the elements of the deque.
[Note 1:
pop_frontandpop_backare erase operations. — end note]-5- Throws: Nothing unless an exception is thrown by
thean assignment operator ofT.-6- Complexity: The number of
calls to the destructor ofobjects of typeTTdestroyed is the same as the number of elements erased, but the number of calls to the assignment operator ofTis no more than the lesser of the number of elements before the erased elements and the number of elements after the erased elements.
Modify 23.3.7.5 [forward.list.modifiers] as indicated:
-1- [...] Inserting
nelements into aforward_listis linear inn, and the number ofcalls to the copy or move constructor ofobjects of typeTconstructed is exactly equal ton. Erasingnelements from aforward_listis linear innand the number ofcalls to the destructor ofobjects of typeTdestroyed is exactly equal ton.
Modify 23.3.11.4 [list.modifiers] as indicated:
-1- Complexity: Insertion of a single element into a list takes constant time and constructs exactly one
call to a constructor ofobject of typeTT. Insertion of multiple elements into a list is linear in the number of elements inserted and the number ofcalls to the copy constructor or move constructor ofobjects of typeTTconstructed is exactly equal to the number of elements inserted.-2- Remarks: Does not affect the validity of iterators and references. If an exception is thrown, there are no effects.
iterator erase(const_iterator position); iterator erase(const_iterator first, const_iterator last); void pop_front(); void pop_back(); void clear() noexcept;-3- Effects: Invalidates only the iterators and references to the erased elements.
-4- Throws: Nothing.
-5- Complexity: Erasing a single element is a constant time operation with a single
call to the destructor ofobject of typeTTdestroyed. Erasing a range in a list is linear time in the size of the range and the number ofcalls to the destructor of typeobjects of typeTTdestroyed is exactly equal to the size of the range.
Modify 23.3.13.2 [vector.cons] as indicated:
template<class InputIterator> constexpr vector(InputIterator first, InputIterator last, const Allocator& = Allocator());-9- Effects: Constructs a
vectorequal to the range [first,last), using the specified allocator.-10- Complexity:
Makes only N calls to the copy constructor ofInitializes exactly N elements (where N is the distance betweenTfirstandlast) and no reallocations if iteratorsfirstandlastare of forward, bidirectional, or random access categories. Itmakesinitializes order Ncalls to the copy constructor ofelements and performs order log N reallocations if they are just input iterators.Ttemplate<container-compatible-range<T> R> constexpr vector(from_range_t, R&& rg, const Allocator& = Allocator());-11- Effects: Constructs a
vectorobject with the elements of the rangerg, using the specified allocator.-12- Complexity: Initializes exactly N elements from the results of dereferencing successive iterators of
rg, where N isranges::distance(rg). Performs no reallocations ifRmodelsranges::forward_rangeorranges::sized_range; otherwise, performs order log N reallocations and initializes order Ncalls to the copy or move constructor ofelements.T
Modify 23.3.13.3 [vector.capacity] as indicated:
constexpr void reserve(size_type n);-3- Preconditions:
Tis CppMoveInsertable intovector.-4- Effects: A directive that informs a
vectorof a planned change in size, so that it can manage the storage allocation accordingly. Afterreserve<del>()</del>,capacity()is greater or equal to the argument ofreserveif reallocation happens; and equal to the previous value ofcapacity()otherwise. Reallocation happens at this point if and only if the current capacity is less than the argument ofreserve<del>()</del>. If an exception is thrown other than by themove constructor of amove-construction of one object of non-Cpp17CopyInsertable typeTfrom another, there are no effects.[...]
constexpr shrink_to_fit();-8- Preconditions:
Tis CppMoveInsertable intovector.-9- Effects:
shrink_to_fitis a non-binding request to reducecapacity()tosize(). [Note 2: The request is non-binding to allow latitude for implementation-specific optimizations. — end note] It does not increasecapacity(), but may reducecapacity()by causing reallocation. If an exception is thrown other than by themove constructormove-construction of one object of a non-Cpp17CopyInsertable typeTfrom another, there are no effects.[...]
constexpr void resize(size_type sz);-14- Preconditions:
Tis Cpp17MoveInsertable and Cpp17DefaultInsertable intovector.-15- Effects: [...]
-16- Remarks: If an exception is thrown other than by the
move constructormove-construction of one object of a non-Cpp17CopyInsertable typeTfrom another, there are no effects.
Modify 23.3.13.5 [vector.modifiers] as indicated:
iterator insert(const_iterator position, const T& x); ... template<container-compatible-range<T> R> void append_range(R&& rg);-1- Complexity: [...]
-2- Remarks: Causes reallocation if the new size is greater than the old capacity. Reallocation invalidates all the references, pointers, and iterators referring to the elements in the sequence, as well as the past-the-end iterator. If no reallocation happens, then references, pointers, and iterators before the insertion point remain valid but those at or after the insertion point, including the past-the-end iterator, are invalidated. If an exception is thrown other than by the
copy constructor, move constructor, assignment operator, or move assignment operator ofthe construction or assignment of one object of typeTor by any \tcode{InputIterator} operationTfrom another, or by anyInputIteratoroperation, there are no effects. If an exception is thrown while inserting a single element at the end andTis Cpp17CopyInsertable oris_nothrow_move_constructible_v<T>istrue, there are no effects. Otherwise, if an exception is thrown by themove constructor of amove-construction of one object of non-Cpp17CopyInsertable typeTfrom another, the effects are unspecified.constexpr iterator erase(const_iterator position); constexpr iterator erase(const_iterator first, const_iterator last); constexpr void pop_back();-3- Effects: Invalidates iterators and references at or after the point of the erase.
-4- Throws: Nothing unless an exception is thrown by the
assignment operator or move assignment operator ofconstruction or assignment of one object of typeTTfrom another.-5- Complexity: The
destructor ofnumber of objects of typeTis called the number of timesTdestroyed is equal to the number of the elements erased, butthean assignment operator ofTis called the number of times equal to the number of elements in the vector after the erased elements.
Modify 23.3.16.5 [inplace.vector.modifiers] as indicated:
constexpr iterator erase(const_iterator position); constexpr iterator erase(const_iterator first, const_iterator last); constexpr void pop_back();-29- Complexity: The
destructor ofnumber of objects of typeTis called the number of timesTdestroyed is equal to the number of the elements erased, butthean assignment operator ofTis called the number of times equal to the number of elements after the erased elements.