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.11.5 [vector.modifiers], 23.3.14.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.9.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 ofT
is 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 typeT
is 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:
T
is CppMoveInsertable intodeque
.-6- Effects:
shrink_to_fit
is 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 typeT
from 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 typeT
T
from 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 typeT
from another, the effects are unspecified.[...]
-5- Throws: Nothing unless an exception is thrown bythe assignment operator ofthe assignment of one object of typeT
T
from 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_front
andpop_back
are 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 typeT
T
destroyed is the same as the number of elements erased, but the number of calls to the assignment operator ofT
is 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
n
elements into aforward_list
is linear inn
, and the number ofcalls to the copy or move constructor ofobjects of typeT
constructed is exactly equal ton
. Erasingn
elements from aforward_list
is linear inn
and the number ofcalls to the destructor ofobjects of typeT
destroyed is exactly equal ton
.
Modify 23.3.9.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 typeT
T
. 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 typeT
T
constructed 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 typeT
T
destroyed. 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 typeT
T
destroyed is exactly equal to the size of the range.
Modify 23.3.11.2 [vector.cons] as indicated:
template<class InputIterator> constexpr vector(InputIterator first, InputIterator last, const Allocator& = Allocator());-9- Effects: Constructs a
vector
equal 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 betweenT
first
andlast
) and no reallocations if iteratorsfirst
andlast
are 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.T
template<container-compatible-range<T> R> constexpr vector(from_range_t, R&& rg, const Allocator& = Allocator());-11- Effects: Constructs a
vector
object 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 ifR
modelsranges::forward_range
orranges::sized_range
; otherwise, performs order log N reallocations and initializes order Ncalls to the copy or move constructor ofelements.T
Modify 23.3.11.3 [vector.capacity] as indicated:
constexpr void reserve(size_type n);-3- Preconditions:
T
is CppMoveInsertable intovector
.-4- Effects: A directive that informs a
vector
of 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 ofreserve
if 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 typeT
from another, there are no effects.[...]
constexpr shrink_to_fit();-8- Preconditions:
T
is CppMoveInsertable intovector
.-9- Effects:
shrink_to_fit
is 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 typeT
from another, there are no effects.[...]
constexpr void resize(size_type sz);-14- Preconditions:
T
is 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 typeT
from another, there are no effects.
Modify 23.3.11.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 typeT
or by any \tcode{InputIterator} operationT
from another, or by anyInputIterator
operation, there are no effects. If an exception is thrown while inserting a single element at the end andT
is 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 typeT
from 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 typeT
T
from another.-5- Complexity: The
destructor ofnumber of objects of typeT
is called the number of timesT
destroyed is equal to the number of the elements erased, butthean assignment operator ofT
is called the number of times equal to the number of elements in the vector after the erased elements.
Modify 23.3.14.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 typeT
is called the number of timesT
destroyed is equal to the number of the elements erased, butthean assignment operator ofT
is called the number of times equal to the number of elements after the erased elements.