Maybe we should add a non-overlapping assertion to that algo (and similar ones). For random access iterators we can check the full precondition, otherwise just check that the iterator for the output range isn't equal to the bound of the input range.
On Thu, 14 Aug 2025, 14:30 Tomasz Kamiński, <tkami...@redhat.com> wrote: > For __n == 0, the elements where self move-assigned by > std::move_backward(__ins, __old_finish - __n, __old_finish). > > PR libstdc++/121313 > > libstdc++-v3/ChangeLog: > > * include/bits/vector.tcc (vector::insert_range): Add check for > empty size. > * testsuite/23_containers/vector/modifiers/insert/insert_range.cc: > New tests. > --- > Testing on x86_64-linux. Newly added test passed. > > OK for trunk and v15. > > libstdc++-v3/include/bits/vector.tcc | 10 ++-- > .../vector/modifiers/insert/insert_range.cc | 50 +++++++++++++++++++ > 2 files changed, 57 insertions(+), 3 deletions(-) > > diff --git a/libstdc++-v3/include/bits/vector.tcc > b/libstdc++-v3/include/bits/vector.tcc > index 70ead1d7083..1543ce9cd77 100644 > --- a/libstdc++-v3/include/bits/vector.tcc > +++ b/libstdc++-v3/include/bits/vector.tcc > @@ -1007,17 +1007,21 @@ _GLIBCXX_BEGIN_NAMESPACE_CONTAINER > > if constexpr (ranges::forward_range<_Rg>) > { > + const auto __ins_idx = __pos - cbegin(); > + // Number of new elements to insert: > + const auto __n = size_type(ranges::distance(__rg)); > + if (__n == 0) > + return begin() + __ins_idx; > + > // Start of existing elements: > pointer __old_start = this->_M_impl._M_start; > // End of existing elements: > pointer __old_finish = this->_M_impl._M_finish; > // Insertion point: > - const auto __ins_idx = __pos - cbegin(); > pointer __ins = __old_start + __ins_idx; > - // Number of new elements to insert: > - const auto __n = size_type(ranges::distance(__rg)); > // Number of elements that can fit in unused capacity: > const auto __cap = this->_M_impl._M_end_of_storage - > __old_finish; > + > if (__cap >= __n) > { > // Number of existing elements after insertion point: > diff --git > a/libstdc++-v3/testsuite/23_containers/vector/modifiers/insert/insert_range.cc > b/libstdc++-v3/testsuite/23_containers/vector/modifiers/insert/insert_range.cc > index 506bebbe519..e4b5982188f 100644 > --- > a/libstdc++-v3/testsuite/23_containers/vector/modifiers/insert/insert_range.cc > +++ > b/libstdc++-v3/testsuite/23_containers/vector/modifiers/insert/insert_range.cc > @@ -99,8 +99,58 @@ test_ranges() > return true; > } > > +struct SelfAssignChecker { > + static int moveCounter; > + static int copyCounter; > + > + SelfAssignChecker() = default; > + constexpr SelfAssignChecker(int v) : val(v) { } > + SelfAssignChecker(const SelfAssignChecker&) = default; > + SelfAssignChecker(SelfAssignChecker&&) = default; > + > + SelfAssignChecker operator=(const SelfAssignChecker& rhs) > + { > + if (this == &rhs) > + ++copyCounter; > + this->val = rhs.val; > + return *this; > + } > + > + SelfAssignChecker operator=(SelfAssignChecker&& rhs) > + { > + if (this == &rhs) > + ++moveCounter; > + this->val = rhs.val; > + return *this; > + } > + > + int val; > + > + friend bool operator==(SelfAssignChecker, SelfAssignChecker) = default; > +}; > + > +int SelfAssignChecker::moveCounter = 0; > +int SelfAssignChecker::copyCounter = 0; > + > +void > +test_pr121313() > +{ > + using namespace __gnu_test; > + > + SelfAssignChecker::copyCounter = SelfAssignChecker::moveCounter = 0; > + do_test<test_forward_range<int>, std::allocator<SelfAssignChecker>>(); > + VERIFY( SelfAssignChecker::moveCounter == 0 ); > + VERIFY( SelfAssignChecker::copyCounter == 0 ); > + > + SelfAssignChecker::copyCounter = SelfAssignChecker::moveCounter = 0; > + do_test<test_input_range<int>, std::allocator<SelfAssignChecker>>(); > + VERIFY( SelfAssignChecker::moveCounter == 0 ); > + VERIFY( SelfAssignChecker::copyCounter == 0 ); > +} > + > int main() > { > test_ranges(); > + test_pr121313(); > static_assert( test_ranges() ); > } > -- > 2.49.0 > >